2 * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
7 * C file for all the UI controls of the mulitiplayer screens
10 * Revision 1.2 2002/05/07 03:16:47 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:10 root
17 * 94 6/16/00 3:16p Jefff
18 * sim of the year dvd version changes, a few german soty localization
21 * 93 10/14/99 2:51p Jefff
24 * 92 10/13/99 3:50p Jefff
25 * fixed unnumbered XSTRs
27 * 91 9/15/99 1:45a Dave
28 * Don't init joystick on standalone. Fixed campaign mode on standalone.
29 * Fixed no-score-report problem in TvT
31 * 90 9/14/99 12:51a Jefff
34 * 89 9/13/99 4:52p Dave
37 * 88 9/13/99 11:30a Dave
38 * Added checkboxes and functionality for disabling PXO banners as well as
39 * disabling d3d zbuffer biasing.
41 * 87 9/12/99 10:06p Jefff
42 * changed instances of "Squad War" to "SquadWar"
44 * 86 9/03/99 1:32a Dave
45 * CD checking by act. Added support to play 2 cutscenes in a row
46 * seamlessly. Fixed super low level cfile bug related to files in the
47 * root directory of a CD. Added cheat code to set campaign mission # in
50 * 85 9/01/99 10:49p Dave
51 * Added nice SquadWar checkbox to the client join wait screen.
53 * 84 8/30/99 2:49p Jefff
55 * 83 8/26/99 8:49p Jefff
56 * Updated medals screen and about everything that ever touches medals in
57 * one way or another. Sheesh.
59 * 82 8/25/99 4:38p Dave
60 * Updated PXO stuff. Make squad war report stuff much more nicely.
62 * 81 8/20/99 2:09p Dave
65 * 80 8/20/99 10:06a Jefff
66 * removed closed/rstricted buttons from multi start screen
68 * 79 8/18/99 11:30a Jefff
70 * 78 8/18/99 10:38a Jefff
72 * 77 8/16/99 4:06p Dave
73 * Big honking checkin.
75 * 76 8/16/99 1:08p Jefff
76 * added sounds to a few controls, made input boxes lose focus on ENTER
78 * 75 8/16/99 9:52a Jefff
79 * fixed bitmap loading on buttons in multi-sync screen
81 * 74 8/11/99 5:54p Dave
82 * Fixed collision problem. Fixed standalone ghost problem.
84 * 73 8/10/99 4:35p Jefff
87 * 72 8/06/99 12:29a Dave
90 * 71 8/05/99 3:13p Jasenw
91 * tweaked some text placement coords.
93 * 70 8/04/99 1:38p Jefff
94 * moved some text in multi join wait
96 * 69 8/03/99 12:45p Dave
99 * 68 7/25/99 5:17p Jefff
100 * campaign descriptions show up on multicreate screen
102 * 67 7/20/99 1:49p Dave
103 * Peter Drake build. Fixed some release build warnings.
105 * 66 7/19/99 2:13p Dave
106 * Added some new strings for Heiko.
108 * 65 7/15/99 9:20a Andsager
109 * FS2_DEMO initial checkin
111 * 64 7/08/99 10:53a Dave
112 * New multiplayer interpolation scheme. Not 100% done yet, but still
113 * better than the old way.
115 * 63 6/30/99 10:49a Jasenw
116 * Fixed coords for new launch countdown ani
118 * 62 6/29/99 7:39p Dave
119 * Lots of small bug fixes.
121 * 61 6/25/99 11:59a Dave
122 * Multi options screen.
124 * 60 6/09/99 2:17p Dave
125 * Fixed up pleasewait bitmap rendering.
127 * 59 6/05/99 3:42p Dave
128 * New multi sync screen.
130 * 58 6/01/99 6:07p Dave
131 * New loading/pause/please wait bar.
133 * 57 5/21/99 6:45p Dave
134 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
135 * start game screen, multi password, and multi pxo-help screen.
137 * 56 5/06/99 11:10a Dave
138 * Fixed coord on multi create screen.
140 * 55 5/04/99 6:38p Dave
141 * Finished multi join-wait screen.
143 * 54 5/04/99 5:20p Dave
144 * Fixed up multiplayer join screen and host options screen. Should both
147 * 53 5/03/99 11:04p Dave
148 * Most of the way done with the multi join screen.
150 * 52 5/03/99 8:32p Dave
151 * New version of multi host options screen.
153 * 51 4/29/99 2:15p Neilk
154 * slider2 code got modified; changed parameters for create
156 * 50 4/25/99 3:02p Dave
157 * Build defines for the E3 build.
159 * 49 4/21/99 6:15p Dave
160 * Did some serious housecleaning in the beam code. Made it ready to go
161 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
162 * a handy macro for recalculating collision pairs for a given object.
164 * 48 4/16/99 5:27p Neilk
165 * added slider support and hir res for multi_create
167 * 47 4/14/99 6:37p Dave
168 * Fixed scroll button bug on host create screen.
170 * 46 4/14/99 5:28p Dave
173 * 45 4/12/99 10:07p Dave
174 * Made network startup more forgiving. Added checkmarks to dogfight
175 * screen for players who hit commit.
177 * 44 4/09/99 2:21p Dave
178 * Multiplayer beta stuff. CD checking.
180 * 43 4/08/99 1:28p Dave
181 * Small bug fixes for refresh button on the multi create screen.
183 * 42 4/08/99 11:55a Neilk
184 * Converted Multi_Create to new artwork (just lowres)
186 * 41 4/08/99 2:10a Dave
187 * Numerous bug fixes for the beta. Added builtin mission info for the
190 * 40 3/20/99 3:48p Andsager
191 * Do mission_loop stuff for PXO
193 * 39 3/10/99 6:50p Dave
194 * Changed the way we buffer packets for all clients. Optimized turret
195 * fired packets. Did some weapon firing optimizations.
197 * 38 3/09/99 6:24p Dave
198 * More work on object update revamping. Identified several sources of
199 * unnecessary bandwidth.
201 * 37 3/08/99 7:03p Dave
202 * First run of new object update system. Looks very promising.
204 * 36 2/25/99 4:19p Dave
205 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
206 * release build warnings. Added more data to the squad war request and
209 * 35 2/24/99 3:26p Anoop
210 * Make sure the host is the only guy who bashes skill level for TvT.
212 * 34 2/24/99 2:25p Dave
213 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
214 * bug for dogfight more.
216 * 33 2/23/99 2:29p Dave
217 * First run of oldschool dogfight mode.
219 * 32 2/17/99 2:11p Dave
220 * First full run of squad war. All freespace and tracker side stuff
223 * 31 2/12/99 6:16p Dave
224 * Pre-mission Squad War code is 95% done.
226 * 30 2/11/99 3:08p Dave
227 * PXO refresh button. Very preliminary squad war support.
229 * 29 2/08/99 5:07p Dave
230 * FS2 chat server support. FS2 specific validated missions.
232 * 28 2/04/99 6:29p Dave
233 * First full working rev of FS2 PXO support. Fixed Glide lighting
236 * 27 1/30/99 5:08p Dave
237 * More new hi-res stuff.Support for nice D3D textures.
239 * 26 1/29/99 2:08a Dave
240 * Fixed beam weapon collisions with players. Reduced size of scoring
241 * struct for multiplayer. Disabled PXO.
243 * 25 1/15/99 2:36p Neilk
244 * fixed multi_jw coordinates
246 * 24 1/13/99 7:19p Neilk
247 * Converted Mission Brief, Barracks, Synch to high res support
249 * 23 1/12/99 7:17p Neilk
251 * 22 1/12/99 5:45p Dave
252 * Moved weapon pipeline in multiplayer to almost exclusively client side.
253 * Very good results. Bandwidth goes down, playability goes up for crappy
254 * connections. Fixed object update problem for ship subsystems.
256 * 21 1/12/99 4:07a Dave
257 * Put in barracks code support for selecting squad logos. Properly
258 * distribute squad logos in a multiplayer game.
260 * 20 1/11/99 7:19p Neilk
261 * Converted multi_join interface to support multiple resolutions
263 * 19 12/18/98 1:13a Dave
264 * Rough 1024x768 support for Direct3D. Proper detection and usage through
267 * 18 12/17/98 4:50p Andsager
268 * Added debrief_assemble_optional_mission_popup_text() for single and
271 * 17 12/14/98 12:13p Dave
272 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
275 * 16 12/10/98 10:19a Andsager
276 * Fix mission loop assert
278 * 15 12/10/98 9:59a Andsager
279 * Fix some bugs with mission loops
281 * 14 12/09/98 1:56p Andsager
282 * Initial checkin of mission loop
284 * 13 12/03/98 5:22p Dave
285 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
288 * 12 11/30/98 1:07p Dave
289 * 16 bit conversion, first run.
291 * 11 11/20/98 11:16a Dave
292 * Fixed up IPX support a bit. Making sure that switching modes and
293 * loading/saving pilot files maintains proper state.
295 * 10 11/19/98 4:57p Dave
296 * Ignore PXO option if IPX is selected.
298 * 9 11/19/98 4:19p Dave
299 * Put IPX sockets back in psnet. Consolidated all multiplayer config
302 * 8 11/19/98 8:04a Dave
303 * Full support for D3-style reliable sockets. Revamped packet lag/loss
304 * system, made it receiver side and at the lowest possible level.
306 * 7 11/17/98 11:12a Dave
307 * Removed player identification by address. Now assign explicit id #'s.
309 * 6 10/19/98 11:15a Dave
310 * Changed requirements for stats storing in PXO mode.
312 * 5 10/16/98 9:40a Andsager
313 * Remove ".h" files from model.h
315 * 4 10/13/98 9:29a Dave
316 * Started neatening up freespace.h. Many variables renamed and
317 * reorganized. Added AlphaColors.[h,cpp]
319 * 3 10/07/98 6:27p Dave
320 * Globalized mission and campaign file extensions. Removed Silent Threat
321 * special code. Moved \cache \players and \multidata into the \data
324 * 2 10/07/98 10:53a Dave
327 * 1 10/07/98 10:50a Dave
329 * 333 10/02/98 3:22p Allender
330 * fix up the -connect option and fix the -port option
332 * 332 9/17/98 9:26p Dave
333 * Externalized new string.
335 * 331 9/17/98 3:08p Dave
336 * PXO to non-pxo game warning popup. Player icon stuff in create and join
337 * game screens. Upped server count refresh time in PXO to 35 secs (from
340 * 330 9/17/98 9:43a Allender
341 * removed an Assert that Dave called bogus.
343 * 329 9/16/98 6:54p Dave
344 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
345 * the ship list box. Added code so that tracker stats are not stored with
348 * 328 9/15/98 7:24p Dave
349 * Minor UI changes. Localized bunch of new text.
351 * 327 9/15/98 4:03p Dave
352 * Changed readyroom and multi screens to display "st" icon for all
353 * missions with mission disk content (not necessarily just those that
354 * come with Silent Threat).
356 * 326 9/15/98 11:44a Dave
357 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
358 * scale factors. Fixed standalone filtering of MD missions to non-MD
361 * 325 9/13/98 9:36p Dave
362 * Support for new info icons for multiplayer missions (from-volition,
363 * valid, mission disk, etc).
365 * 324 9/11/98 4:14p Dave
366 * Fixed file checksumming of < file_size. Put in more verbose kicking and
367 * PXO stats store reporting.
369 * 323 9/10/98 1:17p Dave
370 * Put in code to flag missions and campaigns as being MD or not in Fred
371 * and Freespace. Put in multiplayer support for filtering out MD
372 * missions. Put in multiplayer popups for warning of non-valid missions.
374 * 322 9/04/98 3:51p Dave
375 * Put in validated mission updating and application during stats
378 * 321 8/31/98 2:06p Dave
379 * Make cfile sort the ordering or vp files. Added support/checks for
380 * recognizing "mission disk" players.
382 * 320 8/21/98 1:15p Dave
383 * Put in log system hooks in useful places.
385 * 319 8/20/98 5:31p Dave
386 * Put in handy multiplayer logfile system. Now need to put in useful
387 * applications of it all over the code.
389 * 318 8/12/98 4:53p Dave
390 * Put in 32 bit checksumming for PXO missions. No validation on the
391 * actual tracker yet, though.
393 * 317 8/07/98 10:40a Allender
394 * new command line flags for starting netgames. Only starting currently
395 * works, and PXO isn't implemented yet
397 * 316 7/24/98 9:27a Dave
398 * Tidied up endgame sequencing by removing several old flags and
399 * standardizing _all_ endgame stuff with a single function call.
401 * 315 7/14/98 10:04a Allender
402 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
404 * 314 7/10/98 5:04p Dave
405 * Fix connection speed bug on standalone server.
407 * 313 7/09/98 6:01p Dave
408 * Firsts full version of PXO updater. Put in stub for displaying
411 * 312 7/07/98 2:49p Dave
414 * 311 6/30/98 2:17p Dave
415 * Revised object update system. Removed updates for all weapons. Put
416 * button info back into control info packet.
418 * 310 6/13/98 9:32p Mike
419 * Kill last character in file which caused "Find in Files" to report the
420 * file as "not a text file."
426 #include <winsock.h> // for inet_addr()
429 #include "multiutil.h"
430 #include "multimsgs.h"
436 #include "gamesequence.h"
437 #include "freespace.h"
438 #include "contexthelp.h"
443 #include "missionshipchoice.h"
444 #include "multi_xfer.h"
446 #include "stand_gui.h"
447 #include "linklist.h"
448 #include "multiteamselect.h"
449 #include "missioncampaign.h"
456 #include "missiondebrief.h"
457 #include "multi_ingame.h"
458 #include "multi_kick.h"
459 #include "multi_data.h"
460 #include "multi_campaign.h"
461 #include "multi_team.h"
462 #include "multi_pinfo.h"
463 #include "multi_observer.h"
464 #include "multi_voice.h"
465 #include "multi_endgame.h"
466 #include "managepilot.h"
469 #include "objcollide.h"
471 #include "multi_pmsg.h"
472 #include "multi_obj.h"
473 #include "multi_log.h"
474 #include "alphacolors.h"
475 #include "animplay.h"
476 #include "multi_dogfight.h"
477 #include "missionpause.h"
479 // -------------------------------------------------------------------------------------------------------------
481 // MULTIPLAYER COMMON interface controls
484 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
486 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
495 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
500 #define MULTI_COMMON_TEXT_META_CHAR '$'
501 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
502 #define MULTI_COMMON_TEXT_MAX_LINES 20
503 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
505 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
506 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
508 int Multi_common_top_text_line = -1; // where to start displaying from
509 int Multi_common_num_text_lines = 0; // how many lines we have
511 void multi_common_scroll_text_up();
512 void multi_common_scroll_text_down();
513 void multi_common_move_to_bottom();
514 void multi_common_render_text();
515 void multi_common_split_text();
517 #define MAX_IP_STRING 255 // maximum length for ip string
519 void multi_common_scroll_text_up()
521 Multi_common_top_text_line--;
522 if ( Multi_common_top_text_line < 0 ) {
523 Multi_common_top_text_line = 0;
524 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
525 gamesnd_play_iface(SND_GENERAL_FAIL);
528 gamesnd_play_iface(SND_SCROLL);
532 void multi_common_scroll_text_down()
534 Multi_common_top_text_line++;
535 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
536 Multi_common_top_text_line--;
537 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
538 gamesnd_play_iface(SND_GENERAL_FAIL);
541 gamesnd_play_iface(SND_SCROLL);
545 void multi_common_move_to_bottom()
547 // if there's nowhere to scroll down, do nothing
548 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
552 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
555 void multi_common_set_text(char *str,int auto_scroll)
558 // store the entire string as well
559 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
562 strcpy(Multi_common_all_text,str);
565 // split the whole thing up
566 multi_common_split_text();
568 // scroll to the bottom if we're supposed to
570 multi_common_move_to_bottom();
574 void multi_common_add_text(char *str,int auto_scroll)
577 // store the entire string as well
578 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
581 strcat(Multi_common_all_text,str);
584 // split the whole thing up
585 multi_common_split_text();
587 // scroll to the bottom if we're supposed to
589 multi_common_move_to_bottom();
593 void multi_common_split_text()
596 int n_chars[MAX_BRIEF_LINES];
597 char *p_str[MAX_BRIEF_LINES];
599 n_lines = split_str(Multi_common_all_text, Multi_common_text_coords[gr_screen.res][2], n_chars, p_str, MULTI_COMMON_TEXT_MAX_LINES, MULTI_COMMON_TEXT_META_CHAR);
600 Assert(n_lines != -1);
602 for ( i = 0; i < n_lines; i++ ) {
603 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
604 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
605 Multi_common_text[i][n_chars[i]] = 0;
606 drop_leading_white_space(Multi_common_text[i]);
609 Multi_common_top_text_line = 0;
610 Multi_common_num_text_lines = n_lines;
613 void multi_common_render_text()
615 int i, fh, line_count;
617 fh = gr_get_font_height();
620 gr_set_color_fast(&Color_text_normal);
621 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
622 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
625 gr_string(Multi_common_text_coords[gr_screen.res][0], Multi_common_text_coords[gr_screen.res][1] + (line_count*fh), Multi_common_text[i]);
629 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
630 gr_set_color_fast(&Color_bright_red);
631 gr_string(Multi_common_text_coords[gr_screen.res][0], (Multi_common_text_coords[gr_screen.res][1] + Multi_common_text_coords[gr_screen.res][3])-5, XSTR("more",755));
635 // common notification messaging stuff
636 #define MULTI_COMMON_NOTIFY_TIME 3500
637 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
641 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
646 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
651 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
656 char Multi_common_notify_text[200];
657 int Multi_common_notify_stamp;
659 void multi_common_notify_init()
661 strcpy(Multi_common_notify_text,"");
662 Multi_common_notify_stamp = -1;
665 // add a notification string, drawing appropriately depending on the state/screen we're in
666 void multi_common_add_notify(char *str)
669 strcpy(Multi_common_notify_text,str);
670 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
674 // process/display notification messages
675 void multi_common_notify_do()
677 if(Multi_common_notify_stamp != -1){
678 if(timestamp_elapsed(Multi_common_notify_stamp)){
679 Multi_common_notify_stamp = -1;
682 gr_get_string_size(&w,&h,Multi_common_notify_text);
683 gr_set_color_fast(&Color_white);
685 // determine where it should be placed based upon which screen we're on
687 switch(gameseq_get_state()){
688 case GS_STATE_MULTI_JOIN_GAME :
689 y = Multi_common_join_y[gr_screen.res];
691 case GS_STATE_MULTI_HOST_SETUP :
692 y = Multi_common_create_y[gr_screen.res];
694 case GS_STATE_MULTI_CLIENT_SETUP :
695 y = Multi_common_jw_y[gr_screen.res];
697 case GS_STATE_MULTI_START_GAME :
698 y = Multi_common_msg_y[gr_screen.res];
702 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
709 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
711 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
712 "DotRed", // voice denied
713 "DotGreen", // voice recording
714 "OvalGreen", // team 0
715 "OvalGreen01", // team 0 select
717 "OvalRed01", // team 1 select
718 "mp_coop", // coop mission
719 "mp_teams", // TvT mission
720 "mp_furball", // furball mission
721 "icon-volition", // volition mission
722 "icon-valid", // mission is valid
726 // width and height of the icons
727 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
728 {11, 11}, // voice denied
729 {11, 11}, // voice recording
731 {11, 11}, // team 0 select
733 {11, 11}, // team 1 select
736 {18, 11}, // mp furball
737 {9, 9}, // volition mission
738 {8, 8}, // mission is valid
742 void multi_load_common_icons()
747 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
748 Multi_common_icons[idx] = -1;
749 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
753 void multi_unload_common_icons()
758 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
759 if(Multi_common_icons[idx] != -1){
760 bm_unload(Multi_common_icons[idx]);
761 Multi_common_icons[idx] = -1;
766 // display any relevant voice status icons
767 void multi_common_voice_display_status()
769 switch(multi_voice_status()){
770 // i have been denied the voice token
771 case MULTI_VOICE_STATUS_DENIED:
772 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
773 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED]);
778 // i am currently recording
779 case MULTI_VOICE_STATUS_RECORDING:
780 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
781 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING]);
786 // i am currently playing back sound
787 case MULTI_VOICE_STATUS_PLAYING:
790 // the system is currently idle
791 case MULTI_VOICE_STATUS_IDLE:
797 // palette initialization stuff
798 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
801 int Multi_common_interface_palette = -1;
803 void multi_common_load_palette();
804 void multi_common_set_palette();
805 void multi_common_unload_palette();
807 // load in the palette if it doesn't already exist
808 void multi_common_load_palette()
810 if(Multi_common_interface_palette != -1){
814 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
815 if(Multi_common_interface_palette == -1){
816 nprintf(("Network","Error loading multiplayer common palette!\n"));
820 // set the common palette to be the active one
821 void multi_common_set_palette()
823 // if the palette is not loaded yet, do so now
824 if(Multi_common_interface_palette == -1){
825 multi_common_load_palette();
828 if(Multi_common_interface_palette != -1){
829 #ifndef HARDWARE_ONLY
830 palette_use_bm_palette(Multi_common_interface_palette);
835 // unload the bitmap palette
836 void multi_common_unload_palette()
838 if(Multi_common_interface_palette != -1){
839 bm_unload(Multi_common_interface_palette);
840 Multi_common_interface_palette = -1;
844 void multi_common_verify_cd()
847 // otherwise, call the freespace function to determine if we have a cd
849 if((find_freespace_cd(FS_CDROM_VOLUME_1) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_2) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_3) >= 0) ){
858 // -------------------------------------------------------------------------------------------------------------
860 // MULTIPLAYER JOIN SCREEN
863 #define MULTI_JOIN_NUM_BUTTONS 11
867 #define MULTI_JOIN_PALETTE "InterfacePalette"
869 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
870 "MultiJoin", // GR_640
871 "2_MultiJoin" // GR_1024
874 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
875 "MultiJoin-M", // GR_640
876 "2_MultiJoin-M" // GR_1024
881 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
885 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
895 #define MJ_SCROLL_UP 0
896 #define MJ_SCROLL_DOWN 1
898 #define MJ_SCROLL_INFO_UP 3
899 #define MJ_SCROLL_INFO_DOWN 4
900 #define MJ_JOIN_OBSERVER 5
901 #define MJ_START_GAME 6
907 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
908 int Multi_join_glr_stamp;
910 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
911 int Multi_join_ping_stamp;
912 UI_WINDOW Multi_join_window; // the window object for the join screen
913 UI_BUTTON Multi_join_select_button; // for selecting list items
914 UI_SLIDER2 Multi_join_slider; // handy dandy slider
915 int Multi_join_bitmap; // the background bitmap
917 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
919 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
920 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
921 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
922 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
923 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
924 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
925 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
926 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
927 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
928 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
929 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
932 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
933 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
934 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
935 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
936 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
937 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
938 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
939 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
940 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
941 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
942 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
946 #define MULTI_JOIN_NUM_TEXT 13
948 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
950 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
951 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
952 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
953 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
954 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
955 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
956 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
957 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
958 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
959 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
960 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
961 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
962 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
965 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
966 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
967 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
968 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
969 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
970 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
971 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
972 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
973 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
974 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
975 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
976 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
977 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
981 // constants for coordinate look ups
987 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
988 int Multi_join_sent_stamp;
990 // game information text areas
991 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
996 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1001 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1010 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1019 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1028 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1037 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1046 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1055 // game speed labels
1056 #define MJ_NUM_SPEED_LABELS 5
1057 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1064 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1067 &Color_bright_green,
1068 &Color_bright_green,
1072 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1077 #define IP_CONFIG_FNAME "tcp.cfg" // name of the file which contains known TCP addresses
1079 // extents of the entire boundable game info region
1080 // NOTE : these numbers are completely empirical
1081 #define MJ_PING_GREEN 160
1082 #define MJ_PING_YELLOW 300
1084 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1093 // PXO channel filter
1094 #define MJ_PXO_FILTER_Y 0
1096 // special chars to indicate various status modes for servers
1097 #define MJ_CHAR_STANDALONE "*"
1098 #define MJ_CHAR_CAMPAIGN "c"
1101 // various interface indices
1102 int Multi_join_list_start; // where to start displaying from
1103 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1104 int Multi_join_list_selected; // which item we have selected
1105 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1107 // use this macro to modify the list start
1108 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1109 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1110 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1112 // if we should be sending a join request at the end of the frame
1113 int Multi_join_should_send = -1;
1115 // master tracker details
1116 int Multi_join_frame_count; // keep a count of frames displayed
1117 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1119 // data stuff for auto joining a game
1120 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1121 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1123 int Multi_did_autojoin;
1124 net_addr Multi_autojoin_addr;
1125 int Multi_autojoin_join_stamp;
1126 int Multi_autojoin_query_stamp;
1129 join_request Multi_join_request;
1131 // LOCAL function definitions
1132 void multi_join_check_buttons();
1133 void multi_join_button_pressed(int n);
1134 void multi_join_display_games();
1135 void multi_join_blit_game_status(active_game *game, int y);
1136 void multi_join_load_tcp_addrs();
1137 void multi_join_do_netstuff();
1138 void multi_join_ping_all();
1139 void multi_join_process_select();
1140 void multi_join_list_scroll_up();
1141 void multi_join_list_scroll_down();
1142 void multi_join_list_page_up();
1143 void multi_join_list_page_down();
1144 active_game *multi_join_get_game(int n);
1145 void multi_join_cull_timeouts();
1146 void multi_join_handle_item_cull(active_game *item, int item_index);
1147 void multi_join_send_join_request(int as_observer);
1148 void multi_join_create_game();
1149 void multi_join_blit_top_stuff();
1150 int multi_join_maybe_warn();
1151 int multi_join_warn_pxo();
1152 void multi_join_blit_protocol();
1156 active_game ag, *newitem;;
1159 dc_get_arg(ARG_INT);
1160 for(idx=0; idx<Dc_arg_int; idx++){
1161 // stuff some fake info
1162 memset(&ag, 0, sizeof(active_game));
1163 sprintf(ag.name, "Game %d", idx);
1164 ag.version = MULTI_FS_SERVER_VERSION;
1165 ag.comp_version = MULTI_FS_SERVER_VERSION;
1166 ag.server_addr.addr[0] = (char)idx;
1167 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1170 newitem = multi_update_active_games(&ag);
1172 // timestamp it so we get random timeouts
1173 if(newitem != NULL){
1174 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1179 void multi_join_notify_new_game()
1181 // reset the # of items
1182 Multi_join_slider.set_numberItems(Active_game_count > Mj_max_game_items[gr_screen.res] ? Active_game_count - Mj_max_game_items[gr_screen.res] : 0);
1183 Multi_join_slider.force_currentItem(Multi_join_list_start);
1186 int multi_join_autojoin_do()
1188 // if we have an active game on the list, then return a positive value so that we
1189 // can join the game
1190 if ( Active_game_head && (Active_game_count > 0) ) {
1191 Multi_join_selected_item = Active_game_head;
1195 // send out a server_query again
1196 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1197 send_server_query(&Multi_autojoin_addr);
1198 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1204 void multi_join_game_init()
1208 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1209 // setup various multiplayer things
1210 Assert( Game_mode & GM_MULTIPLAYER );
1211 Assert( Net_player != NULL );
1213 switch (Multi_options_g.protocol) {
1215 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1216 PORT_LENGTH = IPX_PORT_LENGTH;
1220 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1221 PORT_LENGTH = IP_PORT_LENGTH;
1230 memset( &Netgame, 0, sizeof(Netgame) );
1233 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1234 Net_player->player = Player;
1235 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1237 // check for the existence of a CD
1238 multi_common_verify_cd();
1240 // load my local netplayer options
1241 multi_options_local_load(&Net_player->p_info.options, Net_player);
1246 // common_set_interface_palette(MULTI_JOIN_PALETTE);
1248 // destroy any chatbox contents which previously existed (from another game)
1251 // create the interface window
1252 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1253 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1255 // load the background bitmap
1256 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1257 if(Multi_join_bitmap < 0){
1258 // we failed to load the bitmap - this is very bad
1262 // intialize the endgame system
1263 multi_endgame_init();
1265 // initialize the common notification messaging
1266 multi_common_notify_init();
1268 // initialize the common text area
1269 multi_common_set_text("");
1271 // load and use the common interface palette
1272 multi_common_load_palette();
1273 multi_common_set_palette();
1275 // load the help overlay
1276 help_overlay_load(MULTI_JOIN_OVERLAY);
1277 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1279 // do TCP and VMT specific initialization
1280 if(Multi_options_g.protocol == NET_TCP){
1281 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1282 multi_join_load_tcp_addrs();
1285 // initialize any and all timestamps
1286 Multi_join_glr_stamp = -1;
1287 Multi_join_ping_stamp = -1;
1288 Multi_join_sent_stamp = -1;
1290 // reset frame count
1291 Multi_join_frame_count = 0;
1293 // haven't tried to verify on the tracker yet.
1294 Multi_join_mt_tried_verify = 0;
1296 // clear our all game lists to save hassles
1297 multi_join_clear_game_list();
1299 // create the interface buttons
1300 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1301 // create the object
1302 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);
1304 // set the sound to play when highlighted
1305 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1307 // set the ani for the button
1308 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1311 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1315 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1316 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1319 Multi_join_should_send = -1;
1321 // close any previously open chatbox
1324 // create the list item select button
1325 Multi_join_select_button.create(&Multi_join_window, "", Mj_list_area_coords[gr_screen.res][MJ_X_COORD], Mj_list_area_coords[gr_screen.res][MJ_Y_COORD], Mj_list_area_coords[gr_screen.res][MJ_W_COORD], Mj_list_area_coords[gr_screen.res][MJ_H_COORD], 0, 1);
1326 Multi_join_select_button.hide();
1329 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);
1331 // if starting a network game, then go to the create game screen
1332 if ( Cmdline_start_netgame ) {
1333 multi_join_create_game();
1334 } else if ( Cmdline_connect_addr != NULL ) {
1339 // joining a game. Send a join request to the given IP address, and wait for the return.
1340 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1341 Multi_autojoin_addr.type = NET_TCP;
1343 // create the address, looking out for port number at the end
1344 port_num = DEFAULT_GAME_PORT;
1345 p = strrchr(Cmdline_connect_addr, ':');
1349 port_num = (short)atoi(p);
1351 ip_addr = inet_addr(Cmdline_connect_addr);
1352 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1353 Multi_autojoin_addr.port = port_num;
1355 send_server_query(&Multi_autojoin_addr);
1356 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1357 Multi_did_autojoin = 0;
1361 void multi_join_clear_game_list()
1364 Multi_join_list_selected = -1;
1365 Multi_join_selected_item = NULL;
1366 MJ_LIST_START_SET(-1);
1367 Multi_join_list_start_item = NULL;
1369 // free up the active game list
1370 multi_free_active_games();
1372 // initialize the active game list
1373 Active_game_head = NULL;
1374 Active_game_count = 0;
1377 void multi_join_game_do_frame()
1379 // check the status of our reliable socket. If not valid, popup error and return to main menu
1380 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1381 // background for the popup
1382 if ( !psnet_rel_check() ) {
1383 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));
1384 gameseq_post_event(GS_EVENT_MAIN_MENU);
1388 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1389 // all the screens for < 1 second for every screen we automatically move to.
1390 if ( Cmdline_start_netgame ) {
1394 // when joining a network game, wait for the server query to come back, and then join the game
1395 if ( Cmdline_connect_addr != NULL ) {
1398 if ( !Multi_did_autojoin ) {
1399 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1401 // cancel was hit. Send the user back to the main hall
1402 gameseq_post_event(GS_EVENT_MAIN_MENU);
1403 Cmdline_connect_addr = NULL; // reset this value.
1406 // when we get here, we have the data -- join the game.
1407 multi_join_send_join_request(0);
1408 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1409 Multi_did_autojoin = 1;
1412 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1413 multi_join_send_join_request(0);
1414 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1420 // reset the should send var
1421 Multi_join_should_send = -1;
1423 int k = Multi_join_window.process();
1425 // process any keypresses
1428 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1429 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1431 gameseq_post_event(GS_EVENT_MAIN_MENU);
1432 gamesnd_play_iface(SND_USER_SELECT);
1436 // page up the game list
1438 multi_join_list_page_up();
1439 Multi_join_slider.force_currentItem(Multi_join_list_start);
1443 multi_pinfo_popup(Net_player);
1446 // page down the game list
1448 multi_join_list_page_down();
1449 Multi_join_slider.force_currentItem(Multi_join_list_start);
1452 // send out a ping-all
1454 multi_join_ping_all();
1455 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1458 // shortcut to start a game
1460 multi_join_create_game();
1463 // scroll the game list up
1465 multi_join_list_scroll_up();
1466 Multi_join_slider.force_currentItem(Multi_join_list_start);
1469 // scroll the game list down
1471 multi_join_list_scroll_down();
1472 Multi_join_slider.force_currentItem(Multi_join_list_start);
1476 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1477 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1480 // do any network related stuff
1481 multi_join_do_netstuff();
1483 // process any button clicks
1484 multi_join_check_buttons();
1486 // process any list selection stuff
1487 multi_join_process_select();
1489 // draw the background, etc
1491 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1492 if(Multi_join_bitmap != -1){
1493 gr_set_bitmap(Multi_join_bitmap);
1496 Multi_join_window.draw();
1498 // display the active games
1499 multi_join_display_games();
1501 // display any text in the info area
1502 multi_common_render_text();
1504 // display any pending notification messages
1505 multi_common_notify_do();
1507 // blit the CD icon and any PXO filter stuff
1508 multi_join_blit_top_stuff();
1510 // draw the help overlay
1511 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1516 // if we are supposed to be sending a join request
1517 if(Multi_join_should_send != -1){
1518 multi_join_send_join_request(Multi_join_should_send);
1520 Multi_join_should_send = -1;
1522 // increment the frame count
1523 Multi_join_frame_count++;
1526 void multi_join_game_close()
1528 // unload any bitmaps
1529 if(!bm_unload(Multi_join_bitmap)){
1530 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1533 // unload the help overlay
1534 help_overlay_unload(MULTI_JOIN_OVERLAY);
1536 // free up the active game list
1537 multi_free_active_games();
1539 // destroy the UI_WINDOW
1540 Multi_join_window.destroy();
1543 void multi_join_check_buttons()
1546 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1547 // we only really need to check for one button pressed at a time, so we can break after
1549 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1550 multi_join_button_pressed(idx);
1556 void multi_join_button_pressed(int n)
1560 // if we're player PXO, go back there
1561 gameseq_post_event(GS_EVENT_MAIN_MENU);
1562 gamesnd_play_iface(SND_USER_SELECT);
1565 if(Active_game_count <= 0){
1566 multi_common_add_notify(XSTR("No games found!",757));
1567 gamesnd_play_iface(SND_GENERAL_FAIL);
1568 } else if(Multi_join_list_selected == -1){
1569 multi_common_add_notify(XSTR("No game selected!",758));
1570 gamesnd_play_iface(SND_GENERAL_FAIL);
1571 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1572 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1573 gamesnd_play_iface(SND_GENERAL_FAIL);
1575 // otherwise, if he's already played PXO games, warn him
1577 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1578 if(!multi_join_warn_pxo()){
1584 // send the join request here
1585 Assert(Multi_join_selected_item != NULL);
1587 // send a join request packet
1588 Multi_join_should_send = 0;
1590 gamesnd_play_iface(SND_COMMIT_PRESSED);
1596 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1597 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1599 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1603 // scroll the game list up
1605 multi_join_list_scroll_up();
1606 Multi_join_slider.force_currentItem(Multi_join_list_start);
1609 // scroll the game list down
1610 case MJ_SCROLL_DOWN:
1611 multi_join_list_scroll_down();
1612 Multi_join_slider.force_currentItem(Multi_join_list_start);
1615 // scroll the info text box up
1616 case MJ_SCROLL_INFO_UP:
1617 multi_common_scroll_text_up();
1620 // scroll the info text box down
1621 case MJ_SCROLL_INFO_DOWN:
1622 multi_common_scroll_text_down();
1625 // go to the options screen
1627 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1630 // go to the start game screen
1632 multi_join_create_game();
1635 // refresh the game/server list
1637 gamesnd_play_iface(SND_USER_SELECT);
1638 broadcast_game_query();
1641 // join a game as an observer
1642 case MJ_JOIN_OBSERVER:
1643 if(Active_game_count <= 0){
1644 multi_common_add_notify(XSTR("No games found!",757));
1645 gamesnd_play_iface(SND_GENERAL_FAIL);
1646 } else if(Multi_join_list_selected == -1){
1647 multi_common_add_notify(XSTR("No game selected!",758));
1648 gamesnd_play_iface(SND_GENERAL_FAIL);
1649 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1650 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1651 gamesnd_play_iface(SND_GENERAL_FAIL);
1653 // send the join request here
1654 Assert(Multi_join_selected_item != NULL);
1656 Multi_join_should_send = 1;
1658 gamesnd_play_iface(SND_COMMIT_PRESSED);
1663 multi_common_add_notify(XSTR("Not implemented yet!",760));
1664 gamesnd_play_iface(SND_GENERAL_FAIL);
1669 // display all relevant info for active games
1670 void multi_join_display_games()
1672 active_game *moveup = Multi_join_list_start_item;
1676 int y_start = Mj_list_y[gr_screen.res];
1681 // blit the game status (including text and type icon)
1682 multi_join_blit_game_status(moveup,y_start);
1684 // get the connection type
1685 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1686 if((con_type > 4) || (con_type < 0)){
1690 // display the connection speed
1692 strcpy(str, Multi_join_speed_labels[con_type]);
1693 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1694 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1696 // we'll want to have different colors for highlighted items, etc.
1697 if(moveup == Multi_join_selected_item){
1698 gr_set_color_fast(&Color_text_selected);
1700 gr_set_color_fast(&Color_text_normal);
1703 // display the game name, adding appropriate status chars
1705 if(moveup->flags & AG_FLAG_STANDALONE){
1706 strcat(str,MJ_CHAR_STANDALONE);
1708 if(moveup->flags & AG_FLAG_CAMPAIGN){
1709 strcat(str,MJ_CHAR_CAMPAIGN);
1712 // tack on the actual server name
1714 strcat(str,moveup->name);
1715 if(strlen(moveup->mission_name) > 0){
1717 strcat(str,moveup->mission_name);
1720 // make sure the string fits in the display area and draw it
1721 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1722 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1724 // display the ping time
1725 if(moveup->ping.ping_avg > 0){
1726 if(moveup->ping.ping_avg > 1000){
1727 gr_set_color_fast(&Color_bright_red);
1728 strcpy(str,XSTR("> 1 sec",761));
1730 // set the appropriate ping time color indicator
1731 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1732 gr_set_color_fast(&Color_bright_red);
1733 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1734 gr_set_color_fast(&Color_bright_yellow);
1736 gr_set_color_fast(&Color_bright_green);
1739 sprintf(str,"%d",moveup->ping.ping_avg);
1740 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1743 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1746 // display the number of players (be sure to center it)
1747 if(moveup == Multi_join_selected_item){
1748 gr_set_color_fast(&Color_text_selected);
1750 gr_set_color_fast(&Color_text_normal);
1752 sprintf(str,"%d",moveup->num_players);
1753 gr_get_string_size(&w,&h,str);
1754 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);
1758 moveup = moveup->next;
1759 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1761 // if there are no items on the list, display this info
1763 gr_set_color_fast(&Color_bright);
1764 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1768 void multi_join_blit_game_status(active_game *game, int y)
1771 char status_text[25];
1773 // blit the proper icon
1775 switch( game->flags & AG_FLAG_TYPE_MASK ){
1778 if(Multi_common_icons[MICON_COOP] != -1){
1779 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
1784 // team vs. team game
1786 if(Multi_common_icons[MICON_TVT] != -1){
1787 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
1793 case AG_FLAG_DOGFIGHT:
1794 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1795 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
1800 // if we're supposed to draw a bitmap
1802 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1805 // blit the proper status text
1806 memset(status_text,0,25);
1808 switch( game->flags & AG_FLAG_STATE_MASK ){
1809 case AG_FLAG_FORMING:
1810 gr_set_color_fast(&Color_bright_green);
1811 strcpy(status_text,XSTR("Forming",764));
1813 case AG_FLAG_BRIEFING:
1814 gr_set_color_fast(&Color_bright_red);
1815 strcpy(status_text,XSTR("Briefing",765));
1817 case AG_FLAG_DEBRIEF:
1818 gr_set_color_fast(&Color_bright_red);
1819 strcpy(status_text,XSTR("Debrief",766));
1822 gr_set_color_fast(&Color_bright_red);
1823 strcpy(status_text,XSTR("Paused",767));
1825 case AG_FLAG_IN_MISSION:
1826 gr_set_color_fast(&Color_bright_red);
1827 strcpy(status_text,XSTR("Playing",768));
1830 gr_set_color_fast(&Color_bright);
1831 strcpy(status_text,XSTR("Unknown",769));
1834 gr_get_string_size(&str_w,NULL,status_text);
1835 gr_string(Mj_status_coords[gr_screen.res][MJ_X_COORD] + ((Mj_status_coords[gr_screen.res][MJ_W_COORD] - str_w)/2),y,status_text);
1838 // load in a list of active games from our tcp.cfg file
1839 void multi_join_load_tcp_addrs()
1841 char line[MAX_IP_STRING];
1846 // attempt to open the ip list file
1847 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1849 nprintf(("Network","Error loading tcp.cfg file!\n"));
1853 // free up any existing server list
1854 multi_free_server_list();
1856 // read in all the strings in the file
1857 while(!cfeof(file)){
1859 cfgets(line,MAX_IP_STRING,file);
1861 // strip off any newline character
1862 if(line[strlen(line) - 1] == '\n'){
1863 line[strlen(line) - 1] = '\0';
1866 // empty lines don't get processed
1867 if( (line[0] == '\0') || (line[0] == '\n') ){
1871 if ( !psnet_is_valid_ip_string(line) ) {
1872 nprintf(("Network","Invalid ip string (%s)\n",line));
1874 // copy the server ip address
1875 memset(&addr,0,sizeof(net_addr));
1876 addr.type = NET_TCP;
1877 psnet_string_to_addr(&addr,line);
1878 if ( addr.port == 0 ){
1879 addr.port = DEFAULT_GAME_PORT;
1882 // create a new server item on the list
1883 item = multi_new_server_item();
1885 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1893 // do stuff like pinging servers, sending out requests, etc
1894 void multi_join_do_netstuff()
1896 // handle game query stuff
1897 if(Multi_join_glr_stamp == -1){
1898 broadcast_game_query();
1900 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1901 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1903 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1906 // otherwise send out game query and restamp
1907 else if(timestamp_elapsed(Multi_join_glr_stamp)){
1908 broadcast_game_query();
1910 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1911 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1913 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1917 // check to see if we've been accepted. If so, put up message saying so
1918 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
1919 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
1922 // check to see if any join packets we have sent have timed out
1923 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
1924 Multi_join_sent_stamp = -1;
1925 multi_common_add_notify(XSTR("Join request timed out!",771));
1928 // check to see if we should be pinging everyone
1929 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
1930 multi_join_ping_all();
1931 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1935 multi_join_cull_timeouts();
1938 // evaluate a returned pong.
1939 void multi_join_eval_pong(net_addr *addr, fix pong_time)
1942 active_game *moveup = Active_game_head;
1947 if(psnet_same(&moveup->server_addr,addr)){
1949 multi_ping_eval_pong(&moveup->ping);
1953 moveup = moveup->next;
1955 } while(moveup != Active_game_head);
1958 // update the game's ping
1960 if(found && (moveup->ping_end > moveup->ping_start)){
1961 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
1962 moveup->ping_start = -1;
1963 moveup->ping_end = -1;
1968 // ping all the server on the list
1969 void multi_join_ping_all()
1971 active_game *moveup = Active_game_head;
1976 moveup->ping_start = timer_get_fixed_seconds();
1977 moveup->ping_end = -1;
1978 send_ping(&moveup->server_addr);
1980 multi_ping_send(&moveup->server_addr,&moveup->ping);
1982 moveup = moveup->next;
1983 } while(moveup != Active_game_head);
1987 void multi_join_process_select()
1989 // if we don't have anything selected and there are items on the list - select the first one
1990 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
1991 Multi_join_list_selected = 0;
1992 Multi_join_selected_item = multi_join_get_game(0);
1993 MJ_LIST_START_SET(0);
1994 Multi_join_list_start_item = Multi_join_selected_item;
1996 // send a mission description request to this guy
1997 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
1998 multi_common_set_text("");
2000 // I sure hope this doesn't happen
2001 Assert(Multi_join_selected_item != NULL);
2004 // otherwise see if he's clicked on an item
2005 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2007 Multi_join_select_button.get_mouse_pos(NULL,&y);
2009 if(item + Multi_join_list_start < Active_game_count){
2010 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2012 Multi_join_list_selected = item + Multi_join_list_start;
2013 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2015 // I sure hope this doesn't happen
2016 Assert(Multi_join_selected_item != NULL);
2018 // send a mission description request to this guy
2019 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2020 multi_common_set_text("");
2024 // if he's double clicked, then select it and accept
2025 if(Multi_join_select_button.double_clicked()){
2027 Multi_join_select_button.get_mouse_pos(NULL,&y);
2029 if(item == Multi_join_list_selected){
2030 multi_join_button_pressed(MJ_ACCEPT);
2035 // return game n (0 based index)
2036 active_game *multi_join_get_game(int n)
2038 active_game *moveup = Active_game_head;
2045 moveup = moveup->next;
2046 while((moveup != Active_game_head) && (count != n)){
2047 moveup = moveup->next;
2050 if(moveup == Active_game_head){
2051 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2061 // scroll through the game list
2062 void multi_join_list_scroll_up()
2064 // if we're not at the beginning of the list, scroll up
2065 if(Multi_join_list_start_item != Active_game_head){
2066 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2068 MJ_LIST_START_DEC();
2070 gamesnd_play_iface(SND_SCROLL);
2072 gamesnd_play_iface(SND_GENERAL_FAIL);
2076 // scroll through the game list
2077 void multi_join_list_scroll_down()
2079 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2080 Multi_join_list_start_item = Multi_join_list_start_item->next;
2082 MJ_LIST_START_INC();
2084 gamesnd_play_iface(SND_SCROLL);
2086 gamesnd_play_iface(SND_GENERAL_FAIL);
2090 void multi_join_list_page_up()
2092 // in this case, just set us to the beginning of the list
2093 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2094 Multi_join_list_start_item = Active_game_head;
2096 MJ_LIST_START_SET(0);
2098 gamesnd_play_iface(SND_SCROLL);
2100 // otherwise page the whole thing up
2102 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2103 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2105 MJ_LIST_START_DEC();
2107 gamesnd_play_iface(SND_SCROLL);
2111 void multi_join_list_page_down()
2115 // page the whole thing down
2116 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2117 Multi_join_list_start_item = Multi_join_list_start_item->next;
2118 MJ_LIST_START_INC();
2123 gamesnd_play_iface(SND_SCROLL);
2126 void multi_join_cull_timeouts()
2128 active_game *backup;
2130 active_game *moveup = Active_game_head;
2132 // traverse through the entire list if any items exist
2136 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2137 Active_game_count--;
2139 // if this is the head of the list
2140 if(moveup == Active_game_head){
2141 // if this is the _only_ item on the list
2142 if(moveup->next == Active_game_head){
2143 // handle any gui details related to deleting this item
2144 multi_join_handle_item_cull(Active_game_head, count);
2146 free(Active_game_head);
2147 Active_game_head = NULL;
2150 // if there are other items on the list
2152 // handle any gui details related to deleting this item
2153 multi_join_handle_item_cull(moveup, count);
2155 Active_game_head = moveup->next;
2156 Active_game_head->prev = moveup->prev;
2157 Active_game_head->prev->next = Active_game_head;
2159 moveup = Active_game_head;
2162 // if its somewhere else on the list
2164 // handle any gui details related to deleting this item
2165 multi_join_handle_item_cull(moveup, count);
2167 // if its the last item on the list
2168 moveup->next->prev = moveup->prev;
2169 moveup->prev->next = moveup->next;
2171 // if it was the last element on the list, return
2172 if(moveup->next == Active_game_head){
2176 backup = moveup->next;
2182 moveup = moveup->next;
2185 } while(moveup != Active_game_head);
2189 // deep magic begins here.
2190 void multi_join_handle_item_cull(active_game *item, int item_index)
2192 // if this is the only item on the list, unset everything
2193 if(item->next == item){
2194 Multi_join_list_selected = -1;
2195 Multi_join_selected_item = NULL;
2197 Multi_join_slider.set_numberItems(0);
2198 MJ_LIST_START_SET(-1);
2199 Multi_join_list_start_item = NULL;
2205 // see if we should be adjusting our currently selected item
2206 if(item_index <= Multi_join_list_selected){
2207 // the selected item is the head of the list
2208 if(Multi_join_selected_item == Active_game_head){
2209 // move the pointer up since this item is about to be destroyed
2210 Multi_join_selected_item = Multi_join_selected_item->next;
2212 // if this is the item being deleted, select the previous one
2213 if(item == Multi_join_selected_item){
2215 Multi_join_selected_item = Multi_join_selected_item->prev;
2217 // decrement the selected index by 1
2218 Multi_join_list_selected--;
2220 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2221 // 1 less item on the list
2223 // decrement the selected index by 1
2224 Multi_join_list_selected--;
2229 // see if we should be adjusting out current start position
2230 if(item_index <= Multi_join_list_start){
2231 // the start position is the head of the list
2232 if(Multi_join_list_start_item == Active_game_head){
2233 // move the pointer up since this item is about to be destroyed
2234 Multi_join_list_start_item = Multi_join_list_start_item->next;
2236 // if this is the item being deleted, select the previous one
2237 if(item == Multi_join_list_start_item){
2238 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2240 // decrement the starting index by 1
2241 MJ_LIST_START_DEC();
2243 // but decrement the starting index by 1
2244 MJ_LIST_START_DEC();
2249 // maybe go back up a bit so that we always have a full page of items
2250 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2251 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2252 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2253 MJ_LIST_START_DEC();
2257 // set slider location
2258 Multi_join_slider.set_numberItems(Active_game_count > Mj_max_game_items[gr_screen.res] ? Active_game_count - Mj_max_game_items[gr_screen.res] : 0);
2259 Multi_join_slider.force_currentItem(Multi_join_list_start);
2262 void multi_join_send_join_request(int as_observer)
2264 // don't do anything if we have no items selected
2265 if(Multi_join_selected_item == NULL){
2269 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2270 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2271 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2275 memset(&Multi_join_request,0,sizeof(join_request));
2277 // if the netgame is in password mode, put up a request for the password
2278 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2279 if(!multi_passwd_popup(Multi_join_request.passwd)){
2283 nprintf(("Password : %s\n",Multi_join_request.passwd));
2286 // fill out the join request struct
2287 strcpy(Multi_join_request.callsign,Player->callsign);
2288 if(strlen(Player->image_filename) > 0){
2289 strcpy(Multi_join_request.image_filename, Player->image_filename);
2291 if(strlen(Player->squad_filename) > 0){
2292 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2295 // tracker id (if any)
2296 Multi_join_request.tracker_id = Multi_tracker_id;
2298 // player's rank (at least, what he wants you to _believe_)
2299 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2302 Multi_join_request.flags = 0;
2304 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2307 // if the player has hacked data
2308 if(game_hacked_data()){
2309 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2313 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2315 // version of this server
2316 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2318 // server compatible version
2319 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2321 // his local player options
2322 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2324 // set the server address for the netgame
2325 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2327 // send a join request to the guy
2328 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2331 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2334 multi_common_add_notify(XSTR("Sending join request...",773));
2337 void multi_join_create_game()
2339 // maybe warn the player about possible crappy server conditions
2340 if(!multi_join_maybe_warn()){
2344 // make sure to flag ourself as being the master
2345 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2346 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2348 // if we're in PXO mode, mark it down in our player struct
2349 if(MULTI_IS_TRACKER_GAME){
2350 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2351 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2353 // otherwise, if he's already played PXO games, warn him
2356 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2357 if(!multi_join_warn_pxo()){
2364 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2365 gamesnd_play_iface(SND_USER_SELECT);
2368 void multi_join_reset_join_stamp()
2370 // unset the timestamp here so the user can immediately send another join request
2371 Multi_join_sent_stamp = -1;
2372 multi_common_add_notify("");
2375 void multi_join_blit_top_stuff()
2377 // blit the cd icon if he has one
2378 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2381 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2383 gr_set_bitmap(Multi_common_icons[MICON_CD]);
2384 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2388 #define CW_CODE_CANCEL 0 // cancel the action
2389 #define CW_CODE_OK 1 // continue anyway
2390 #define CW_CODE_INFO 2 // gimme some more information
2392 #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)
2393 #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)
2395 #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)
2396 #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)
2398 int multi_join_warn_update_low(int code)
2402 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2405 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2408 return CW_CODE_CANCEL;
2411 int multi_join_warn_update_medium(int code)
2415 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2418 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2421 return CW_CODE_CANCEL;
2424 int multi_join_maybe_warn()
2428 // if the player is set for low updates
2429 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2432 code = multi_join_warn_update_low(code);
2433 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2438 // if the player is set for medium updates
2439 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2442 code = multi_join_warn_update_medium(code);
2443 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2451 int multi_join_warn_pxo()
2453 // 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;
2457 void multi_join_blit_protocol()
2459 gr_set_color_fast(&Color_bright);
2461 switch(Socket_type){
2464 gr_string(5, 2, "TCP");
2468 gr_string(5, 2, "IPX");
2474 // -------------------------------------------------------------------------------------------------
2476 // MULTIPLAYER START GAME screen
2481 #define MULTI_SG_PALETTE "InterfacePalette"
2483 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2484 "MultiStartGame", // GR_640
2485 "2_MultiStartGame" // GR_1024
2488 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2489 "MultiStartGame-M", // GR_640
2490 "2_MultiStartGame-M" // GR_1024
2495 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2500 // constants for coordinate look ups
2501 #define MSG_X_COORD 0
2502 #define MSG_Y_COORD 1
2503 #define MSG_W_COORD 2
2504 #define MSG_H_COORD 3
2508 // input password field
2509 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2518 // input game title field
2519 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2528 // rank selected field
2529 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2539 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2550 #define MULTI_SG_NUM_BUTTONS 10
2551 #define MSG_OPEN_GAME 0
2552 //#define MSG_CLOSED_GAME 1
2553 //#define MSG_RESTRICTED_GAME 2
2554 #define MSG_PASSWD_GAME 1
2555 #define MSG_RANK_SET_GAME 2
2556 #define MSG_RANK_SCROLL_UP 3
2557 #define MSG_RANK_SCROLL_DOWN 4
2558 #define MSG_RANK_ABOVE 5
2559 #define MSG_RANK_BELOW 6
2561 #define MSG_OPTIONS 8
2562 #define MSG_ACCEPT 9
2564 UI_WINDOW Multi_sg_window; // the window object for the join screen
2565 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2566 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2567 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2568 int Multi_sg_bitmap; // the background bitmap
2570 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2572 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2573 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2574 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2575 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2576 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2577 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2578 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2579 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2580 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2581 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2582 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2583 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2586 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2587 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2588 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2589 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2590 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2591 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2592 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2593 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2594 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2595 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2596 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2597 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2601 #define MULTI_SG_NUM_TEXT 11
2602 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2604 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2605 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2606 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2607 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2608 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2609 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2610 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2611 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2612 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2613 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2614 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2615 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2616 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2619 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2620 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2621 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2622 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2623 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2624 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2625 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2626 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2627 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2628 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2629 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2630 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2631 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2635 // starting index for displaying ranks
2636 int Multi_sg_rank_start;
2637 int Multi_sg_rank_select;
2639 // netgame pointer to indirect through
2640 netgame_info *Multi_sg_netgame;
2642 // hold temporary values in this structure when on a standalone server
2643 netgame_info Multi_sg_netgame_temp;
2645 // forward declarations
2646 void multi_sg_check_buttons();
2647 void multi_sg_button_pressed(int n);
2648 void multi_sg_init_gamenet();
2649 void multi_sg_draw_radio_buttons();
2650 void multi_sg_rank_scroll_up();
2651 void multi_sg_rank_scroll_down();
2652 void multi_sg_rank_display_stuff();
2653 void multi_sg_rank_process_select();
2654 void multi_sg_rank_build_name(char *in,char *out);
2655 void multi_sg_check_passwd();
2656 void multi_sg_check_name();
2657 void multi_sg_release_passwd();
2658 int multi_sg_rank_select_valid(int rank);
2659 void multi_sg_select_rank_default();
2661 // function which takes a rank name and returns the index. Useful for commandline options
2662 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2663 // the rank isn't found, we return -1
2664 int multi_start_game_rank_from_name( char *rank ) {
2667 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2668 if ( !stricmp(Ranks[i].name, rank) ) {
2676 void multi_start_game_init()
2680 // initialize the gamenet
2681 multi_sg_init_gamenet();
2683 // create the interface window
2684 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2685 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2687 // load the background bitmap
2688 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2689 if(Multi_sg_bitmap < 0){
2690 // we failed to load the bitmap - this is very bad
2694 // initialize the common notification messaging
2695 multi_common_notify_init();
2697 // initialize the common text area
2698 multi_common_set_text("");
2700 // use the common interface palette
2701 multi_common_set_palette();
2703 // create the interface buttons
2704 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2705 // create the object
2706 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);
2708 // set the sound to play when highlighted
2709 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2711 // set the ani for the button
2712 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2715 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2719 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2720 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2723 // load the help overlay
2724 help_overlay_load(MULTI_START_OVERLAY);
2725 help_overlay_set_state(MULTI_START_OVERLAY,0);
2727 // intiialize the rank selection items
2728 multi_sg_select_rank_default();
2729 Multi_sg_rank_start = Multi_sg_rank_select;
2731 // create the rank select button
2732 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);
2733 Multi_sg_rank_button.hide();
2735 // create the netgame name input box
2736 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);
2738 // create the netgame password input box, and disable it by default
2739 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);
2740 Multi_sg_game_passwd.hide();
2741 Multi_sg_game_passwd.disable();
2743 // set the netgame text to this gadget and make it have focus
2744 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2745 Multi_sg_game_name.set_focus();
2747 // if starting a netgame, set the name of the game and any other options that are appropriate
2748 if ( Cmdline_start_netgame ) {
2749 if ( Cmdline_game_name != NULL ) {
2750 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2751 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2754 // deal with the different game types -- only one should even be active, so we will just go down
2755 // the line. Last one wins.
2756 if ( Cmdline_closed_game ) {
2757 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2758 } else if ( Cmdline_restricted_game ) {
2759 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2760 } else if ( Cmdline_game_password != NULL ) {
2761 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2762 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2763 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2766 // deal with rank above and rank below
2767 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2771 if ( Cmdline_rank_above != NULL ) {
2772 rank_str = Cmdline_rank_above;
2774 rank_str = Cmdline_rank_below;
2777 // try and get the rank index from the name -- if found, then set the rank base
2778 // and the game type. apparently we only support either above or below, not both
2779 // together, so I make a random choice
2780 rank = multi_start_game_rank_from_name( rank_str );
2782 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2784 // now an arbitrary decision
2785 if ( Cmdline_rank_above != NULL ) {
2786 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2788 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2793 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2797 void multi_start_game_do()
2799 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2800 // all the screens for < 1 second for every screen we automatically move to.
2801 if ( Cmdline_start_netgame ) {
2805 int k = Multi_sg_window.process();
2807 // process any keypresses
2810 if(help_overlay_active(MULTI_START_OVERLAY)){
2811 help_overlay_set_state(MULTI_START_OVERLAY,0);
2813 gamesnd_play_iface(SND_USER_SELECT);
2814 multi_quit_game(PROMPT_NONE);
2819 case KEY_LCTRL + KEY_ENTER :
2820 case KEY_RCTRL + KEY_ENTER :
2821 gamesnd_play_iface(SND_COMMIT_PRESSED);
2822 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2826 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2827 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2830 // check to see if the user has selected a different rank
2831 multi_sg_rank_process_select();
2833 // check any button presses
2834 multi_sg_check_buttons();
2836 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2837 multi_sg_check_passwd();
2838 multi_sg_check_name();
2840 // draw the background, etc
2842 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
2843 if(Multi_sg_bitmap != -1){
2844 gr_set_bitmap(Multi_sg_bitmap);
2847 Multi_sg_window.draw();
2849 // display rank stuff
2850 multi_sg_rank_display_stuff();
2852 // display any pending notification messages
2853 multi_common_notify_do();
2855 // draw all radio button
2856 multi_sg_draw_radio_buttons();
2858 // draw the help overlay
2859 help_overlay_maybe_blit(MULTI_START_OVERLAY);
2865 void multi_start_game_close()
2867 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
2868 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2869 multi_options_update_start_game(Multi_sg_netgame);
2872 // unload any bitmaps
2873 if(!bm_unload(Multi_sg_bitmap)){
2874 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
2877 // unload the help overlay
2878 help_overlay_unload(MULTI_START_OVERLAY);
2880 // destroy the UI_WINDOW
2881 Multi_sg_window.destroy();
2884 void multi_sg_check_buttons()
2887 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
2888 // we only really need to check for one button pressed at a time, so we can break after
2890 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
2891 multi_sg_button_pressed(idx);
2897 void multi_sg_button_pressed(int n)
2900 // go to the options screen
2902 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
2907 if(!help_overlay_active(MULTI_START_OVERLAY)){
2908 help_overlay_set_state(MULTI_START_OVERLAY,1);
2910 help_overlay_set_state(MULTI_START_OVERLAY,0);
2914 // the open button was pressed
2916 // if the closed option is selected
2917 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
2918 Multi_sg_netgame->mode = NG_MODE_OPEN;
2920 gamesnd_play_iface(SND_USER_SELECT);
2922 // release the password control if necessary
2923 multi_sg_release_passwd();
2925 // if its already selected
2927 gamesnd_play_iface(SND_GENERAL_FAIL);
2932 // the open button was pressed
2933 case MSG_CLOSED_GAME:
2934 // if the closed option is selected
2935 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
2936 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2938 gamesnd_play_iface(SND_USER_SELECT);
2940 // release the password control if necessary
2941 multi_sg_release_passwd();
2943 // if its already selected
2945 gamesnd_play_iface(SND_GENERAL_FAIL);
2949 // toggle password protection
2950 case MSG_PASSWD_GAME:
2951 // if we selected it
2952 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
2953 gamesnd_play_iface(SND_USER_SELECT);
2955 Multi_sg_game_passwd.enable();
2956 Multi_sg_game_passwd.unhide();
2957 Multi_sg_game_passwd.set_focus();
2959 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2961 // copy in the current network password
2962 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2964 gamesnd_play_iface(SND_GENERAL_FAIL);
2969 // toggle "restricted" on or off
2970 case MSG_RESTRICTED_GAME:
2971 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
2972 gamesnd_play_iface(SND_USER_SELECT);
2973 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2975 // release the password control if necessary
2976 multi_sg_release_passwd();
2978 gamesnd_play_iface(SND_GENERAL_FAIL);
2982 // turn off all rank requirements
2983 case MSG_RANK_SET_GAME:
2984 // if either is set, then turn then both off
2985 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
2986 gamesnd_play_iface(SND_USER_SELECT);
2988 // set it to the default case if we're turning it off
2989 multi_sg_select_rank_default();
2990 Multi_sg_rank_start = Multi_sg_rank_select;
2992 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2994 // release the password control if necessary
2995 multi_sg_release_passwd();
2997 gamesnd_play_iface(SND_GENERAL_FAIL);
3001 // rank above was pressed
3002 case MSG_RANK_ABOVE :
3003 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3004 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3006 // select the first item
3007 multi_sg_select_rank_default();
3008 Multi_sg_rank_start = Multi_sg_rank_select;
3011 gamesnd_play_iface(SND_USER_SELECT);
3013 gamesnd_play_iface(SND_GENERAL_FAIL);
3017 // rank below was pressed
3018 case MSG_RANK_BELOW :
3019 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3020 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3022 // select the first item
3023 multi_sg_select_rank_default();
3024 Multi_sg_rank_start = Multi_sg_rank_select;
3027 gamesnd_play_iface(SND_USER_SELECT);
3029 gamesnd_play_iface(SND_GENERAL_FAIL);
3033 // scroll the rank list up
3034 case MSG_RANK_SCROLL_UP:
3035 multi_sg_rank_scroll_up();
3038 // scroll the rank list down
3039 case MSG_RANK_SCROLL_DOWN:
3040 multi_sg_rank_scroll_down();
3043 // move to the create game screen
3045 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3046 gamesnd_play_iface(SND_COMMIT_PRESSED);
3050 gamesnd_play_iface(SND_GENERAL_FAIL);
3051 multi_common_add_notify(XSTR("Not implemented yet!",760));
3056 // NOTE : this is where all Netgame initialization should take place on the host
3057 void multi_sg_init_gamenet()
3059 char buf[128],out_name[128];
3061 net_player *server_save;
3063 // back this data up in case we are already connected to a standalone
3064 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3065 server_save = Netgame.server;
3067 // remove campaign flags
3068 Game_mode &= ~(GM_CAMPAIGN_MODE);
3070 // clear out the Netgame structure and start filling in the values
3071 memset( &Netgame, 0, sizeof(Netgame) );
3072 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3074 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3075 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3076 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3077 Multi_sg_netgame = &Netgame;
3080 ml_string(NOX("Starting netgame as Host/Server"));
3082 Multi_sg_netgame = &Multi_sg_netgame_temp;
3086 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3087 char *server_addr = inet_ntoa(temp_addr);
3088 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3091 Net_player->tracker_player_id = Multi_tracker_id;
3093 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3094 Multi_sg_netgame->mode = NG_MODE_OPEN;
3095 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3096 if(Multi_sg_netgame->security < 16){
3097 Multi_sg_netgame->security += 16;
3100 // set the version_info field
3101 Multi_sg_netgame->version_info = NG_VERSION_ID;
3103 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3104 Netgame.host = Net_player;
3107 // set the default netgame flags
3108 Multi_sg_netgame->flags = 0;
3110 // intialize endgame stuff
3111 multi_endgame_init();
3113 // load in my netgame options
3114 multi_options_netgame_load(&Netgame.options);
3116 // load my local netplayer options
3117 multi_options_local_load(&Net_player->p_info.options, Net_player);
3119 // setup the default game name, taking care of string length and player callsigns
3120 memset(out_name,0,128);
3122 pilot_format_callsign_personal(Player->callsign,out_name);
3123 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3124 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3125 strcpy(buf, XSTR("Temporary name",783));
3127 strcpy(Multi_sg_netgame->name, buf);
3129 // set the default qos and duration
3130 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3132 // make sure to set the server correctly (me or the standalone)
3133 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3134 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3135 Netgame.server = Net_player;
3136 Net_player->player_id = multi_get_new_id();
3138 // setup debug flags
3139 Netgame.debug_flags = 0;
3141 if(!Cmdline_server_firing){
3142 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3144 if(!Cmdline_client_dodamage){
3145 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3149 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3150 Netgame.server = server_save;
3153 // if I have a cd or not
3155 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3158 // if I have hacked data
3159 if(game_hacked_data()){
3160 Net_player->flags |= NETINFO_FLAG_HAXOR;
3163 // assign my player struct and other data
3164 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3165 Net_player->s_info.voice_token_timestamp = -1;
3167 // if we're supposed to flush our cache directory, do so now
3168 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3169 multi_flush_multidata_cache();
3172 ml_string(NOX("Flushing multi-data cache"));
3178 void multi_sg_draw_radio_buttons()
3180 // draw the appropriate radio button
3181 switch(Multi_sg_netgame->mode){
3183 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3186 case NG_MODE_CLOSED:
3187 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3190 case NG_MODE_PASSWORD:
3191 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3193 /* case NG_MODE_RESTRICTED:
3194 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3197 case NG_MODE_RANK_ABOVE:
3198 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3199 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3201 case NG_MODE_RANK_BELOW:
3202 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3203 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3208 void multi_sg_rank_scroll_up()
3210 // if he doesn't have either of the rank flags set, then ignore this
3211 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3215 if(Multi_sg_rank_start > 0){
3216 Multi_sg_rank_start--;
3217 gamesnd_play_iface(SND_SCROLL);
3219 gamesnd_play_iface(SND_GENERAL_FAIL);
3223 void multi_sg_rank_scroll_down()
3225 // if he doesn't have either of the rank flags set, then ignore this
3226 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3230 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3231 Multi_sg_rank_start++;
3232 gamesnd_play_iface(SND_SCROLL);
3234 gamesnd_play_iface(SND_GENERAL_FAIL);
3238 void multi_sg_rank_display_stuff()
3243 // if he doesn't have either of the rank flags set, then ignore this
3244 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3248 // display the list of ranks
3249 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3250 idx = Multi_sg_rank_start;
3252 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3253 // if its the selected item, then color it differently
3254 if(idx == Multi_sg_rank_select){
3255 gr_set_color_fast(&Color_text_selected);
3257 gr_set_color_fast(&Color_text_normal);
3261 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3262 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3270 // display the selected rank
3272 gr_set_color_fast(&Color_bright);
3273 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3274 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3278 void multi_sg_rank_process_select()
3282 // if he doesn't have either of the rank flags set, then ignore this
3283 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3287 // see if he's clicked on an item on the rank list
3288 if(Multi_sg_rank_button.pressed()){
3290 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3293 if(item + Multi_sg_rank_start < NUM_RANKS){
3294 // evaluate whether this rank is valid for the guy to pick
3295 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3296 gamesnd_play_iface(SND_USER_SELECT);
3298 Multi_sg_rank_select = item + Multi_sg_rank_start;
3300 // set the Netgame rank
3301 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3303 gamesnd_play_iface(SND_GENERAL_FAIL);
3305 memset(string,0,255);
3306 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3307 multi_common_add_notify(string);
3313 void multi_sg_rank_build_name(char *in,char *out)
3319 first = strtok(use," ");
3321 // just copy the string
3326 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3327 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3328 first = strtok(NULL, NOX("\n"));
3330 // if he's not just a plain lieutenant
3332 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3335 // if he _is_ just a plain lieutenant
3344 void multi_sg_check_passwd()
3346 // check to see if the password input box has been pressed
3347 if(Multi_sg_game_passwd.changed()){
3348 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3352 void multi_sg_check_name()
3354 // check to see if the game name input box has been pressed
3355 if(Multi_sg_game_name.changed()){
3356 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3360 void multi_sg_release_passwd()
3362 // hide and disable the password input box
3363 Multi_sg_game_passwd.hide();
3364 Multi_sg_game_passwd.disable();
3366 // set the focus back to the name input box
3367 Multi_sg_game_name.set_focus();
3370 int multi_sg_rank_select_valid(int rank)
3373 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3374 if(Net_player->player->stats.rank >= rank){
3380 if(Net_player->player->stats.rank <= rank){
3388 void multi_sg_select_rank_default()
3390 // pick our rank for now
3391 Multi_sg_rank_select = Net_player->player->stats.rank;
3393 // set the Netgame rank
3394 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3397 // -------------------------------------------------------------------------------------------------
3399 // MULTIPLAYER CREATE GAME screen
3404 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3405 "MultiCreate", // GR_640
3406 "2_MultiCreate" // GR_1024
3409 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3410 "MultiCreate-M", // GR_640
3411 "2_MultiCreate-M" // GR_1024
3414 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3415 "PleaseWait", // GR_640
3416 "2_PleaseWait" // GR_1024
3420 #define MULTI_CREATE_NUM_BUTTONS 23
3423 #define MC_SHOW_ALL 0
3424 #define MC_SHOW_COOP 1
3425 #define MC_SHOW_TEAM 2
3426 #define MC_SHOW_DOGFIGHT 3
3427 #define MC_PXO_REFRESH 4
3428 #define MC_PILOT_INFO 5
3429 #define MC_SCROLL_LIST_UP 6
3430 #define MC_SCROLL_LIST_DOWN 7
3431 #define MC_SCROLL_PLAYERS_UP 8
3432 #define MC_SCROLL_PLAYERS_DOWN 9
3433 #define MC_MISSION_FILTER 10
3434 #define MC_CAMPAIGN_FILTER 11
3435 #define MC_CANCEL 12
3440 #define MC_SCROLL_INFO_UP 17
3441 #define MC_SCROLL_INFO_DOWN 18
3442 #define MC_HOST_OPTIONS 19
3444 #define MC_OPTIONS 21
3445 #define MC_ACCEPT 22
3448 UI_WINDOW Multi_create_window; // the window object for the create screen
3449 UI_BUTTON Multi_create_player_select_button; // for selecting players
3450 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3451 int Multi_create_bitmap; // the background bitmap
3452 UI_SLIDER2 Multi_create_slider; // for create list
3454 // constants for coordinate look ups
3455 #define MC_X_COORD 0
3456 #define MC_Y_COORD 1
3457 #define MC_W_COORD 2
3458 #define MC_H_COORD 3
3460 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3462 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3463 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3464 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3465 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3466 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3467 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3468 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3469 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3470 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3471 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3472 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3473 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3474 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3475 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3476 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3477 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3478 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3479 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3480 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3481 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3482 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3483 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3484 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3487 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3488 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3489 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3490 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3491 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3492 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3493 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3494 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3495 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3496 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3497 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3498 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3499 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3500 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3501 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3502 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3503 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3504 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3505 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3506 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3507 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3508 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3509 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3513 #define MULTI_CREATE_NUM_TEXT 15
3514 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3516 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3517 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3518 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3519 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3520 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3521 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3522 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3523 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3524 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3525 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3526 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3527 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3528 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3529 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3530 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3533 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3534 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3535 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3536 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3537 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3538 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3539 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3540 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3541 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3542 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3543 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3544 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3545 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3546 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3547 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3551 // squad war checkbox
3552 UI_CHECKBOX Multi_create_sw_checkbox;
3553 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3557 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3565 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3574 // game information text areas
3575 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3584 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3593 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3602 // mission icon stuff
3603 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3605 38, -2 // y is an offset
3608 61, -2 // y is an offset
3612 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3614 61, -1 // y is an offset
3617 98, 1 // y is an offset
3621 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3623 72, 0 // y is an offset
3626 115, 0 // y is an offset
3630 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3632 91, 0 // y is an offset
3635 146, 0 // y is an offset
3639 // mission/campaign list column areas
3640 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3645 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3650 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3655 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3660 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3665 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3670 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3671 {13, 116}, // GR_640
3672 {21, 186} // GR_1024
3675 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3676 {467, 150}, // GR_640
3677 {747, 240} // GR_1024
3680 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3681 {484, 342}, // GR_640
3682 {774, 547} // GR_1024
3685 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3687 3, 197, 13, 105 // GR_640
3690 5, 316, 20, 168 // GR_1024
3694 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3699 // player list control thingie defs
3700 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3701 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3702 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3704 // master tracker details
3705 int Multi_create_frame_count; // framecount
3706 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3708 // mission filter settings
3709 int Multi_create_filter; // what mode we're in
3711 // game/campaign list control defs
3712 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3718 int Multi_create_list_count; // number of items in listbox
3719 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3720 int Multi_create_list_start; // where to start displaying from
3721 int Multi_create_list_select; // which item is currently highlighted
3722 int Multi_create_files_loaded;
3724 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3726 int Multi_create_mission_count; // how many we have
3727 int Multi_create_campaign_count;
3728 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3729 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3731 // use a pointer for the file list. Will point to either the missions or the campaigns
3732 multi_create_info *Multi_create_file_list;
3734 // LOCAL function definitions
3735 void multi_create_check_buttons();
3736 void multi_create_button_pressed(int n);
3737 void multi_create_init_as_server();
3738 void multi_create_init_as_client();
3739 void multi_create_do_netstuff();
3740 void multi_create_plist_scroll_up();
3741 void multi_create_plist_scroll_down();
3742 void multi_create_plist_process();
3743 void multi_create_plist_blit_normal();
3744 void multi_create_plist_blit_team();
3745 void multi_create_list_scroll_up();
3746 void multi_create_list_scroll_down();
3747 void multi_create_list_do();
3748 void multi_create_list_select_item(int n);
3749 void multi_create_list_blit_icons(int list_index, int y_start);
3750 void multi_create_accept_hit();
3751 void multi_create_draw_filter_buttons();
3752 void multi_create_set_selected_team(int team);
3753 short multi_create_get_mouse_id();
3754 int multi_create_ok_to_commit();
3755 int multi_create_verify_cds();
3756 void multi_create_refresh_pxo();
3757 void multi_create_sw_clicked();
3759 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3760 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3761 void multi_create_select_to_filename(int select_index,char *filename);
3762 int multi_create_select_to_index(int select_index);
3764 int Multi_create_should_show_popup = 0;
3767 // sorting function to sort mission lists.. Basic sorting on mission name
3768 int multi_create_sort_func(const void *a, const void *b)
3770 multi_create_info *m1, *m2;
3772 m1 = (multi_create_info *)a;
3773 m2 = (multi_create_info *)b;
3775 return ( strcmp(m1->name, m2->name) );
3778 void multi_create_setup_list_data(int mode)
3780 int idx,should_sort,switched_modes;
3782 // set the current mode
3785 if((Multi_create_list_mode != mode) && (mode != -1)){
3786 Multi_create_list_mode = mode;
3789 // set up the list pointers
3790 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
3791 Multi_create_file_list = Multi_create_mission_list;
3792 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
3793 Multi_create_file_list = Multi_create_campaign_list;
3799 // get the mission count based upon the filter selected
3800 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
3801 switch(Multi_create_filter){
3802 case MISSION_TYPE_MULTI:
3803 Multi_create_list_count = Multi_create_mission_count;
3806 Multi_create_list_count = 0;
3807 // find all missions which match
3808 for(idx=0;idx<Multi_create_mission_count;idx++){
3809 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
3810 Multi_create_list_count++;
3814 // if we switched modes and we have more than 0 items, sort them
3815 if(switched_modes && (Multi_create_list_count > 0)){
3820 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
3821 switch(Multi_create_filter){
3822 case MISSION_TYPE_MULTI:
3823 Multi_create_list_count = Multi_create_campaign_count;
3826 Multi_create_list_count = 0;
3827 // find all missions which match
3828 for(idx=0;idx<Multi_create_campaign_count;idx++){
3829 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
3830 Multi_create_list_count++;
3834 // if we switched modes and we have more than 0 items, sort them
3835 if(switched_modes && (Multi_create_list_count > 0)){
3842 // reset the list start and selected indices
3843 Multi_create_list_start = 0;
3844 Multi_create_list_select = -1;
3845 multi_create_list_select_item(Multi_create_list_start);
3847 // sort the list of missions if necessary
3849 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
3853 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);
3856 void multi_create_game_init()
3861 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
3862 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3863 multi_create_init_as_server();
3865 multi_create_init_as_client();
3868 // initialize the player list data
3869 Multi_create_plist_select_flag = 0;
3870 Multi_create_plist_select_id = -1;
3872 // create the interface window
3873 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
3874 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
3876 // load the background bitmap
3877 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
3878 if(Multi_create_bitmap < 0){
3879 // we failed to load the bitmap - this is very bad
3883 // close any previous existing instances of the chatbox and create a new one
3887 // load the help overlay
3888 help_overlay_load(MULTI_CREATE_OVERLAY);
3889 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
3891 // initialize the common notification messaging
3892 multi_common_notify_init();
3894 // use the common interface palette
3895 multi_common_set_palette();
3897 // create the interface buttons
3898 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
3899 b = &Multi_create_buttons[gr_screen.res][idx];
3901 // create the object
3902 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
3904 // set the sound to play when highlighted
3905 b->button.set_highlight_action(common_play_highlight_sound);
3907 // set the ani for the button
3908 b->button.set_bmaps(b->filename);
3911 b->button.link_hotspot(b->hotspot);
3913 // some special case stuff for the pxo refresh button
3914 if(idx == MC_PXO_REFRESH){
3915 // if not a PXO game, or if I'm not a server disable and hide the button
3916 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
3918 b->button.disable();
3924 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
3925 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
3928 // if this is a PXO game, enable the squadwar checkbox
3929 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);
3930 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
3931 if(!MULTI_IS_TRACKER_GAME){
3932 Multi_create_sw_checkbox.hide();
3933 Multi_create_sw_checkbox.disable();
3937 // disable squad war button in demo
3938 Multi_create_sw_checkbox.hide();
3939 Multi_create_sw_checkbox.disable();
3942 // initialize the mission type filtering mode
3943 Multi_create_filter = MISSION_TYPE_MULTI;
3945 // initialize the list mode, and load in a list
3946 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3947 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3948 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
3949 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3950 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3952 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
3953 Multi_create_list_start = -1;
3954 Multi_create_list_select = -1;
3955 Multi_create_list_count = 0;
3957 Multi_create_slider.create(&Multi_create_window, Mc_slider_coords[gr_screen.res][MC_X_COORD], Mc_slider_coords[gr_screen.res][MC_Y_COORD], Mc_slider_coords[gr_screen.res][MC_W_COORD],Mc_slider_coords[gr_screen.res][MC_H_COORD], MULTI_CREATE_MAX_LIST_ITEMS, Mc_slider_bitmap[gr_screen.res], &multi_create_list_scroll_up, &multi_create_list_scroll_down, NULL);
3959 // create the player list select button
3960 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);
3961 Multi_create_player_select_button.hide();
3963 // create the mission/campaign list select button
3964 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);
3965 Multi_create_list_select_button.hide();
3967 // set hotkeys for a couple of things.
3968 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
3970 // init some master tracker stuff
3971 Multi_create_frame_count = 0;
3972 Multi_create_mt_tried_login = 0;
3974 // remove campaign flags
3975 Game_mode &= ~(GM_CAMPAIGN_MODE);
3977 // send any pilots as appropriate
3978 multi_data_send_my_junk();
3979 Multi_create_file_list = Multi_create_mission_list;
3981 Multi_create_campaign_count = 0;
3982 Multi_create_mission_count = 0;
3983 Multi_create_files_loaded = 0;
3986 void multi_create_game_do()
3989 char *loading_str = XSTR("Loading", 1336);
3992 // set this if we want to show the pilot info popup
3993 Multi_create_should_show_popup = 0;
3995 // first thing is to load the files
3996 if ( !Multi_create_files_loaded ) {
3997 // if I am a client, send a list request to the server for the missions
3998 if ( MULTIPLAYER_CLIENT ) {
3999 send_mission_list_request( MISSION_LIST_REQUEST );
4003 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4005 // draw the background, etc
4007 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4008 if(Multi_create_bitmap != -1){
4009 gr_set_bitmap(Multi_create_bitmap);
4013 if ( loading_bitmap > -1 ){
4014 gr_set_bitmap(loading_bitmap);
4016 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4018 // draw "Loading" on it
4019 gr_set_color_fast(&Color_normal);
4021 gr_get_string_size(&str_w, &str_h, loading_str);
4022 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4027 multi_create_list_load_missions();
4028 multi_create_list_load_campaigns();
4030 // if this is a tracker game, validate missions
4031 if(MULTI_IS_TRACKER_GAME){
4032 multi_update_valid_missions();
4035 // update the file list
4036 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4039 // don't bother setting netgame state if ont the server
4040 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4041 Netgame.game_state = NETGAME_STATE_FORMING;
4042 send_netgame_update_packet();
4045 // if we're on the standalone we have to tell him that we're now in the host setup screen
4046 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4047 send_netplayer_update_packet();
4049 Multi_create_files_loaded = 1;
4052 int k = chatbox_process();
4053 k = Multi_create_window.process(k,0);
4056 // same as the cancel button
4058 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4059 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4061 gamesnd_play_iface(SND_USER_SELECT);
4062 multi_quit_game(PROMPT_HOST);
4067 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4068 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4071 // process any button clicks
4072 multi_create_check_buttons();
4074 // do any network related stuff
4075 multi_create_do_netstuff();
4077 // draw the background, etc
4079 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4080 if(Multi_create_bitmap != -1){
4081 gr_set_bitmap(Multi_create_bitmap);
4085 // if we're not in team vs. team mode, don't draw the team buttons
4086 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4087 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4088 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4089 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4090 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4092 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4093 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4094 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4095 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4098 // draw the window itself
4099 Multi_create_window.draw();
4101 gr_set_color_fast(&Color_normal);
4103 // draw Create Game text
4104 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));
4106 // draw players text
4107 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4109 // draw players text
4110 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4112 // process and display the player list
4113 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4114 multi_create_plist_process();
4115 if(Netgame.type_flags & NG_TYPE_TEAM){
4116 multi_create_plist_blit_team();
4118 multi_create_plist_blit_normal();
4121 // process and display the game/campaign list
4122 multi_create_list_do();
4124 // draw the correct mission filter button
4125 multi_create_draw_filter_buttons();
4127 // display any text in the info area
4128 multi_common_render_text();
4130 // display any pending notification messages
4131 multi_common_notify_do();
4133 // force the correct mission/campaign button to light up
4134 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4135 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4137 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4140 // force draw the closed button if it is toggled on
4141 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4142 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4145 // process and show the chatbox thingie
4149 Multi_create_window.draw_tooltip();
4151 // display the voice status indicator
4152 multi_common_voice_display_status();
4154 // blit the help overlay if necessary
4155 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4158 if(MULTI_IS_TRACKER_GAME){
4159 if(Netgame.type_flags & NG_TYPE_SW){
4160 gr_set_color_fast(&Color_bright);
4162 gr_set_color_fast(&Color_normal);
4164 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4170 // if we're supposed to show the pilot info popup, do it now
4171 if(Multi_create_should_show_popup){
4172 // get the player index and address of the player item the mouse is currently over
4173 if(Multi_create_plist_select_flag){
4174 player_index = find_player_id(Multi_create_plist_select_id);
4175 if(player_index != -1){
4176 multi_pinfo_popup(&Net_players[player_index]);
4181 // increment the frame count
4182 Multi_create_frame_count++;
4185 void multi_create_game_close()
4187 // unload any bitmaps
4188 if(!bm_unload(Multi_create_bitmap)){
4189 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4192 // unload the help overlay
4193 help_overlay_unload(MULTI_CREATE_OVERLAY);
4195 // destroy the chatbox
4198 // destroy the UI_WINDOW
4199 Multi_create_window.destroy();
4202 void multi_create_check_buttons()
4205 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4206 // we only really need to check for one button pressed at a time, so we can break after
4208 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4209 multi_create_button_pressed(idx);
4214 // if the squad war checkbox was clicked
4215 if(Multi_create_sw_checkbox.changed()){
4216 multi_create_sw_clicked();
4220 void multi_create_button_pressed(int n)
4226 gamesnd_play_iface(SND_USER_SELECT);
4227 multi_quit_game(PROMPT_HOST);
4230 // if valid commit conditions have not been met
4231 if(!multi_create_ok_to_commit()){
4236 multi_create_accept_hit();
4241 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4242 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4244 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4248 // scroll the info text box up
4249 case MC_SCROLL_INFO_UP:
4250 multi_common_scroll_text_up();
4253 // scroll the info text box down
4254 case MC_SCROLL_INFO_DOWN:
4255 multi_common_scroll_text_down();
4258 // scroll the player list up
4259 case MC_SCROLL_PLAYERS_UP:
4260 multi_create_plist_scroll_up();
4263 // scroll the player list down
4264 case MC_SCROLL_PLAYERS_DOWN:
4265 multi_create_plist_scroll_down();
4268 // scroll the game/campaign list up
4269 case MC_SCROLL_LIST_UP:
4270 multi_create_list_scroll_up();
4271 Multi_create_slider.forceUp(); // move slider up
4274 // scroll the game/campaign list down
4275 case MC_SCROLL_LIST_DOWN:
4276 multi_create_list_scroll_down();
4277 Multi_create_slider.forceDown(); // move slider down
4280 // go to the options screen
4282 gamesnd_play_iface(SND_USER_SELECT);
4283 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4286 // show all missions
4288 if(Multi_create_filter != MISSION_TYPE_MULTI){
4289 gamesnd_play_iface(SND_USER_SELECT);
4290 Multi_create_filter = MISSION_TYPE_MULTI;
4291 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4293 gamesnd_play_iface(SND_GENERAL_FAIL);
4297 // show cooperative missions
4299 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4300 gamesnd_play_iface(SND_USER_SELECT);
4301 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4302 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4304 gamesnd_play_iface(SND_GENERAL_FAIL);
4308 // show team vs. team missions
4310 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4311 gamesnd_play_iface(SND_USER_SELECT);
4312 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4313 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4315 gamesnd_play_iface(SND_GENERAL_FAIL);
4319 // show dogfight missions
4320 case MC_SHOW_DOGFIGHT:
4321 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4322 gamesnd_play_iface(SND_USER_SELECT);
4323 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4324 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4326 gamesnd_play_iface(SND_GENERAL_FAIL);
4330 // toggle temporary netgame closed on/off
4332 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4333 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4335 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4336 multi_options_update_netgame();
4338 gamesnd_play_iface(SND_USER_SELECT);
4341 // kick the currently selected player (if possible)
4343 // lookup the player at the specified index
4344 if(Multi_create_plist_select_flag){
4345 idx = find_player_id(Multi_create_plist_select_id);
4346 // kick him - but don't ban him
4348 multi_kick_player(idx,0);
4353 // switch to individual mission mode and load in a list
4354 case MC_MISSION_FILTER:
4355 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4356 Netgame.campaign_mode = MP_SINGLE;
4358 gamesnd_play_iface(SND_USER_SELECT);
4360 // update the file list
4361 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4363 gamesnd_play_iface(SND_GENERAL_FAIL);
4367 // switch to campaign mode and load in a list
4368 case MC_CAMPAIGN_FILTER:
4369 // switch off squad war
4370 Multi_create_sw_checkbox.set_state(0);
4371 Netgame.type_flags = NG_TYPE_COOP;
4373 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4374 Netgame.campaign_mode = MP_CAMPAIGN;
4376 gamesnd_play_iface(SND_USER_SELECT);
4378 // update the file list
4379 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4381 gamesnd_play_iface(SND_GENERAL_FAIL);
4385 // attempt to set the selected player's team
4387 multi_create_set_selected_team(0);
4388 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4389 multi_team_send_update();
4393 // attempt to set the selected player's team
4395 multi_create_set_selected_team(1);
4396 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4397 multi_team_send_update();
4401 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4403 Multi_create_should_show_popup = 1;
4406 // go to the host options screen
4407 case MC_HOST_OPTIONS:
4408 gamesnd_play_iface(SND_USER_SELECT);
4409 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4412 // refresh PXO file list
4413 case MC_PXO_REFRESH:
4414 if(!MULTI_IS_TRACKER_GAME){
4417 multi_create_refresh_pxo();
4421 gamesnd_play_iface(SND_GENERAL_FAIL);
4422 multi_common_add_notify(XSTR("Not implemented yet!",760));
4427 // do stuff like pinging servers, sending out requests, etc
4428 void multi_create_do_netstuff()
4432 // if not on a standalone
4433 void multi_create_init_as_server()
4435 // set me up as the host and master
4436 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4439 // if on a standalone
4440 void multi_create_init_as_client()
4442 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4445 // scroll up through the player list
4446 void multi_create_plist_scroll_up()
4448 gamesnd_play_iface(SND_GENERAL_FAIL);
4451 // scroll down through the player list
4452 void multi_create_plist_scroll_down()
4454 gamesnd_play_iface(SND_GENERAL_FAIL);
4457 void multi_create_plist_process()
4459 int test_count,idx,player_index;
4461 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4463 for(idx=0;idx<MAX_PLAYERS;idx++){
4464 // count anyone except the standalone server (if applicable)
4465 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4469 if(test_count <= 0){
4473 // if we had a selected item but that player has left, select myself instead
4474 if(Multi_create_plist_select_flag){
4475 player_index = find_player_id(Multi_create_plist_select_id);
4476 if(player_index == -1){
4477 Multi_create_plist_select_id = Net_player->player_id;
4480 Multi_create_plist_select_flag = 1;
4481 Multi_create_plist_select_id = Net_player->player_id;
4484 // if the player has clicked somewhere in the player list area
4485 if(Multi_create_player_select_button.pressed()){
4488 // get the player index and address of the player item the mouse is currently over
4489 player_id = multi_create_get_mouse_id();
4490 player_index = find_player_id(player_id);
4491 if(player_index != -1){
4492 Multi_create_plist_select_flag = 1;
4493 Multi_create_plist_select_id = player_id;
4498 void multi_create_plist_blit_normal()
4501 char str[CALLSIGN_LEN+5];
4502 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4505 // display all the players
4506 for(idx=0;idx<MAX_PLAYERS;idx++){
4507 // count anyone except the standalone server (if applicable)
4508 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4512 // highlight him if he's the host
4513 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4514 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4515 gr_set_color_fast(&Color_text_active_hi);
4517 gr_set_color_fast(&Color_bright);
4520 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4521 gr_set_color_fast(&Color_text_active);
4523 gr_set_color_fast(&Color_text_normal);
4527 // optionally draw his CD status
4528 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4529 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4530 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4532 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4535 // make sure the string will fit, then display it
4536 strcpy(str,Net_players[idx].player->callsign);
4537 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4538 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4540 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4541 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4548 void multi_create_plist_blit_team()
4551 char str[CALLSIGN_LEN+1];
4552 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4555 // display all the red players first
4556 for(idx=0;idx<MAX_PLAYERS;idx++){
4557 // count anyone except the standalone server (if applicable)
4558 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4559 // reset total offset
4562 // highlight him if he's the host
4563 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4564 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4565 gr_set_color_fast(&Color_text_active_hi);
4567 // be sure to blit the correct team button
4568 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4570 gr_set_color_fast(&Color_bright);
4573 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4574 gr_set_color_fast(&Color_text_active);
4576 // be sure to blit the correct team button
4577 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4579 gr_set_color_fast(&Color_text_normal);
4583 // optionally draw his CD status
4584 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4585 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4586 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4588 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4591 // blit the red team indicator
4592 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4593 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4594 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
4595 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4597 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4600 if(Multi_common_icons[MICON_TEAM0] != -1){
4601 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
4602 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4604 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4608 // make sure the string will fit
4609 strcpy(str,Net_players[idx].player->callsign);
4610 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4611 strcat(str,XSTR("(O)",787));
4613 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4615 // display him in the correct half of the list depending on his team
4616 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4621 // display all the green players next
4622 for(idx=0;idx<MAX_PLAYERS;idx++){
4623 // count anyone except the standalone server (if applicable)
4624 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4625 // reset total offset
4628 // highlight him if he's the host
4629 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4630 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4631 gr_set_color_fast(&Color_text_active_hi);
4633 // be sure to blit the correct team button
4634 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4636 gr_set_color_fast(&Color_bright);
4639 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4640 gr_set_color_fast(&Color_text_active);
4642 // be sure to blit the correct team button
4643 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4645 gr_set_color_fast(&Color_text_normal);
4649 // optionally draw his CD status
4650 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4651 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4652 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4654 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4657 // blit the red team indicator
4658 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4659 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4660 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
4661 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4663 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4666 if(Multi_common_icons[MICON_TEAM1] != -1){
4667 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
4668 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4670 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4674 // make sure the string will fit
4675 strcpy(str,Net_players[idx].player->callsign);
4676 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4677 strcat(str,XSTR("(O)",787));
4679 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4681 // display him in the correct half of the list depending on his team
4682 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4688 void multi_create_list_scroll_up()
4690 if(Multi_create_list_start > 0){
4691 Multi_create_list_start--;
4693 gamesnd_play_iface(SND_SCROLL);
4695 gamesnd_play_iface(SND_GENERAL_FAIL);
4699 void multi_create_list_scroll_down()
4701 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4702 Multi_create_list_start++;
4704 gamesnd_play_iface(SND_SCROLL);
4706 gamesnd_play_iface(SND_GENERAL_FAIL);
4710 // gets a list of multiplayer misisons
4711 void multi_create_list_load_missions()
4713 char *fname, mission_name[NAME_LENGTH+1];
4714 char wild_card[256];
4717 memset(wild_card, 0, 256);
4718 strcpy(wild_card, NOX("*"));
4719 strcat(wild_card, FS_MISSION_FILE_EXT);
4720 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4721 Multi_create_mission_count = 0;
4723 // maybe create a standalone dialog
4724 if(Game_mode & GM_STANDALONE_SERVER){
4725 std_create_gen_dialog("Loading missions");
4726 std_gen_set_text("Mission:", 1);
4729 for(idx = 0; idx < file_count; idx++){
4730 int flags,max_players;
4734 fname = Multi_create_files_array[idx];
4736 // tack on any necessary file extension
4737 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4739 // for multiplayer beta builds, only accept builtin missions
4740 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4741 if(game_find_builtin_mission(filename) == NULL){
4744 #elif defined(PD_BUILD)
4745 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4750 if(Game_mode & GM_STANDALONE_SERVER){
4751 std_gen_set_text(filename, 2);
4754 flags = mission_parse_is_multi(filename, mission_name);
4756 // if the mission is a multiplayer mission, then add it to the mission list
4758 max_players = mission_parse_get_multi_mission_info( filename );
4759 m_respawn = The_mission.num_respawns;
4761 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4762 multi_create_info *mcip;
4764 mcip = &Multi_create_mission_list[Multi_create_mission_count];
4765 strcpy(mcip->filename, filename );
4766 strcpy(mcip->name, mission_name );
4767 mcip->flags = flags;
4768 mcip->respawn = m_respawn;
4769 mcip->max_players = (ubyte)max_players;
4771 // get any additional information for possibly builtin missions
4772 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4776 Multi_create_mission_count++;
4781 Multi_create_slider.set_numberItems(Multi_create_mission_count > Multi_create_list_max_display[gr_screen.res] ? Multi_create_mission_count-Multi_create_list_max_display[gr_screen.res] : 0);
4783 // maybe create a standalone dialog
4784 if(Game_mode & GM_STANDALONE_SERVER){
4785 std_destroy_gen_dialog();
4789 void multi_create_list_load_campaigns()
4792 int idx, file_count;
4793 int campaign_type,max_players;
4795 char wild_card[256];
4797 // maybe create a standalone dialog
4798 if(Game_mode & GM_STANDALONE_SERVER){
4799 std_create_gen_dialog("Loading campaigns");
4800 std_gen_set_text("Campaign:", 1);
4803 Multi_create_campaign_count = 0;
4804 memset(wild_card, 0, 256);
4805 strcpy(wild_card, NOX("*"));
4806 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
4807 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4808 for(idx = 0; idx < file_count; idx++){
4810 char *filename, name[NAME_LENGTH];
4812 fname = Multi_create_files_array[idx];
4814 // tack on any necessary file extension
4815 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
4817 // for multiplayer beta builds, only accept builtin missions
4818 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4819 if(game_find_builtin_mission(filename) == NULL){
4822 #elif defined(PD_BUILD)
4823 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4828 if(Game_mode & GM_STANDALONE_SERVER){
4829 std_gen_set_text(filename, 2);
4832 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
4833 flags = mission_campaign_parse_is_multi( filename, name );
4834 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
4835 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4836 multi_create_info *mcip;
4838 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
4839 strcpy(mcip->filename, filename );
4840 strcpy(mcip->name, name );
4842 // setup various flags
4843 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
4844 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
4845 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
4846 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
4848 Int3(); // bogus campaign multi type -- find allender
4851 // 0 respawns for campaign files (should be contained within the mission files themselves)
4854 // 0 max players for campaign files
4855 mcip->max_players = (unsigned char)max_players;
4857 // get any additional information for possibly builtin missions
4858 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4862 Multi_create_campaign_count++;
4867 // maybe create a standalone dialog
4868 if(Game_mode & GM_STANDALONE_SERVER){
4869 std_destroy_gen_dialog();
4873 void multi_create_list_do()
4876 int start_index,stop_index;
4877 char selected_name[255];
4879 // bail early if there aren't any selectable items
4880 if(Multi_create_list_count == 0){
4884 // first check to see if the user has clicked on an item
4885 if(Multi_create_list_select_button.pressed()){
4887 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
4890 // make sure we are selectedin valid indices
4891 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
4892 item += Multi_create_list_start;
4894 if(item < Multi_create_list_count){
4895 multi_create_list_select_item(item);
4896 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
4901 // bail early if we don't have a start position
4902 if(Multi_create_list_start == -1){
4906 // display the list of individual campaigns/missions
4908 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
4910 start_index = multi_create_select_to_index(Multi_create_list_start);
4911 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
4912 for(idx=start_index; idx<stop_index; idx++){
4913 // see if we should drop out
4914 if(count == Multi_create_list_max_display[gr_screen.res]){
4918 // see if we should filter out this mission
4919 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
4923 // highlight the selected item
4924 multi_create_select_to_filename(Multi_create_list_select,selected_name);
4925 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
4926 gr_set_color_fast(&Color_text_selected);
4928 gr_set_color_fast(&Color_text_normal);
4931 // draw the type icon
4932 multi_create_list_blit_icons(idx, y_start);
4934 // force fit the mission name string
4935 strcpy(selected_name,Multi_create_file_list[idx].name);
4936 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
4937 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
4939 // draw the max players if in mission mode
4940 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
4941 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
4943 // force fit the mission filename string
4944 strcpy(selected_name,Multi_create_file_list[idx].filename);
4945 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
4946 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
4953 // takes care of stuff like changing indices around and setting up the netgame structure
4954 void multi_create_list_select_item(int n)
4956 int abs_index,campaign_type,max_players;
4957 char title[NAME_LENGTH+1];
4958 netgame_info ng_temp;
4961 char *campaign_desc;
4963 // if not on the standalone server
4964 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4967 // on the standalone
4969 memset(&ng_temp,0,sizeof(netgame_info));
4973 if ( n != Multi_create_list_select ) {
4974 // check to see if this is a valid index, and bail if it is not
4975 abs_index = multi_create_select_to_index(n);
4976 if(abs_index == -1){
4980 Multi_create_list_select = n;
4982 // set the mission name
4983 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4984 multi_create_select_to_filename(n,ng->mission_name);
4986 multi_create_select_to_filename(n,ng->campaign_name);
4989 // make sure the netgame type is properly set
4990 int old_type = Netgame.type_flags;
4991 abs_index = multi_create_select_to_index(n);
4992 if(abs_index != -1){
4993 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
4994 // if we're in squad war mode, leave it as squad war
4995 if(old_type & NG_TYPE_SW){
4996 ng->type_flags = NG_TYPE_SW;
4998 ng->type_flags = NG_TYPE_TVT;
5000 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5001 ng->type_flags = NG_TYPE_COOP;
5002 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5003 ng->type_flags = NG_TYPE_DOGFIGHT;
5007 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5008 if(!(ng->type_flags & NG_TYPE_TEAM)){
5009 Multi_create_sw_checkbox.set_state(0);
5012 // if we switched from something else to team vs. team mode, do some special processing
5013 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5017 switch(Multi_create_list_mode){
5018 case MULTI_CREATE_SHOW_MISSIONS:
5019 // don't forget to update the info box window thingie
5020 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5021 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5022 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5024 Assert(ng->max_players > 0);
5025 strcpy(ng->title,The_mission.name);
5027 // set the information area text
5028 multi_common_set_text(The_mission.mission_desc);
5030 // if we're on the standalone, send a request for the description
5032 send_netgame_descript_packet(&Netgame.server_addr,0);
5033 multi_common_set_text("");
5036 // set the respawns as appropriate
5037 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5038 ng->respawn = Netgame.options.respawn;
5039 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5041 ng->respawn = Multi_create_file_list[abs_index].respawn;
5042 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5045 case MULTI_CREATE_SHOW_CAMPAIGNS:
5046 // if not on the standalone server
5047 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5048 // get the campaign info
5049 memset(title,0,NAME_LENGTH+1);
5050 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5051 memset(ng->campaign_name,0,NAME_LENGTH+1);
5052 ng->max_players = 0;
5054 // if we successfully got the # of players
5056 memset(ng->title,0,NAME_LENGTH+1);
5057 strcpy(ng->title,title);
5058 ng->max_players = max_players;
5061 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5063 // set the information area text
5064 // multi_common_set_text(ng->title);
5065 multi_common_set_text(campaign_desc);
5067 // if on the standalone server, send a request for the description
5069 // no descriptions currently kept for campaigns
5072 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5077 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5079 send_netgame_update_packet();
5081 // update all machines about stuff like respawns, etc.
5082 multi_options_update_netgame();
5084 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5089 void multi_create_list_blit_icons(int list_index, int y_start)
5091 multi_create_info *mcip;
5092 fs_builtin_mission *fb;
5095 // get a pointer to the list item
5096 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5097 if((list_index < 0) || (list_index > max_index)){
5100 mcip = &Multi_create_file_list[list_index];
5102 // blit the multiplayer type icons
5103 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5104 if(Multi_common_icons[MICON_COOP] >= 0){
5105 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
5106 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5108 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5109 if(Multi_common_icons[MICON_TVT] >= 0){
5110 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
5111 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5113 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5114 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5115 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
5116 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5120 // if its a valid mission, blit the valid mission icon
5121 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5122 if(Multi_common_icons[MICON_VALID] >= 0){
5123 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
5124 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5128 // now see if its a builtin mission
5129 fb = game_find_builtin_mission(mcip->filename);
5130 // if the mission is from volition, blit the volition icon
5131 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5132 if(Multi_common_icons[MICON_VOLITION] >= 0){
5133 gr_set_bitmap(Multi_common_icons[MICON_VOLITION]);
5134 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5139 void multi_create_accept_hit()
5141 char selected_name[255];
5142 int start_campaign = 0;
5144 // make sure all players have finished joining
5145 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5146 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5147 gamesnd_play_iface(SND_GENERAL_FAIL);
5150 gamesnd_play_iface(SND_COMMIT_PRESSED);
5153 // do single mission stuff
5154 switch(Multi_create_list_mode){
5155 case MULTI_CREATE_SHOW_MISSIONS:
5156 if(Multi_create_list_select != -1){
5157 // set the netgame mode
5158 Netgame.campaign_mode = MP_SINGLE;
5160 // setup various filenames and mission names
5161 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5162 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5163 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5166 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5168 multi_common_add_notify(XSTR("No mission selected!",789));
5173 case MULTI_CREATE_SHOW_CAMPAIGNS:
5174 // do campaign related stuff
5175 if(Multi_create_list_select != -1){
5176 // set the netgame mode
5177 Netgame.campaign_mode = MP_CAMPAIGN;
5179 // start a campaign instead of a single mission
5180 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5181 multi_campaign_start(selected_name);
5185 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5187 multi_common_add_notify(XSTR("No campaign selected!",790));
5193 // if this is a team vs team situation, lock the players send a final team update
5194 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5195 multi_team_host_lock_all();
5196 multi_team_send_update();
5199 // if not on the standalone, move to the mission sync state which will take care of everything
5200 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5201 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5202 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5204 // otherwise tell the standalone to do so
5206 // when the standalone receives this, he'll do the mission syncing himself
5207 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5211 void multi_create_draw_filter_buttons()
5213 // highlight the correct filter button
5214 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5215 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5216 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5217 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5218 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5219 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5220 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5221 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5227 void multi_create_set_selected_team(int team)
5231 // if we don't currently have a player selected, don't do anything
5232 if(!Multi_create_plist_select_flag){
5233 gamesnd_play_iface(SND_GENERAL_FAIL);
5236 gamesnd_play_iface(SND_USER_SELECT);
5238 // otherwise attempt to set the team for this guy
5239 player_index = find_player_id(Multi_create_plist_select_id);
5240 if(player_index != -1){
5241 multi_team_set_team(&Net_players[player_index],team);
5245 void multi_create_handle_join(net_player *pl)
5247 // for now just play a bloop sound
5248 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5251 // fill in net address of player the mouse is over, return player index (or -1 if none)
5252 short multi_create_get_mouse_id()
5254 // determine where he clicked (y pixel value)
5256 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5258 // select things a little differently if we're in team vs. team or non-team vs. team mode
5260 if(Netgame.type_flags & NG_TYPE_TEAM){
5261 int player_index = -1;
5263 // look through all of team red first
5264 for(idx=0;idx<MAX_PLAYERS;idx++){
5265 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5268 // if this is the _nth_ guy
5276 // if we still haven't found him yet, look through the green team
5277 if(player_index == -1){
5278 for(idx=0;idx<MAX_PLAYERS;idx++){
5279 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5281 // if this is the _nth_ guy
5290 if(player_index != -1){
5291 return Net_players[player_index].player_id;
5294 // select the nth active player if possible, disregarding the standalone server
5295 for(idx=0;idx<MAX_PLAYERS;idx++){
5296 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5299 // if this is the _nth_ guy
5301 return Net_players[idx].player_id;
5310 void multi_create_select_to_filename(int select_index,char *filename)
5314 // look through the mission list
5315 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5316 for(idx=0;idx<Multi_create_mission_count;idx++){
5317 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5321 // if we found the item
5322 if(select_index < 0){
5323 strcpy(filename,Multi_create_file_list[idx].filename);
5328 // look through the campaign list
5329 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5330 for(idx=0;idx<Multi_create_campaign_count;idx++){
5333 // if we found the item
5334 if(select_index < 0){
5335 strcpy(filename,Multi_create_file_list[idx].filename);
5341 strcpy(filename,"");
5344 int multi_create_select_to_index(int select_index)
5347 int lookup_index = 0;
5349 // look through the mission list
5350 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5351 for(idx=0;idx<Multi_create_mission_count;idx++){
5352 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5356 // if we found the item
5357 if(select_index < lookup_index){
5362 // look through the campaign list
5363 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5364 for(idx=0;idx<Multi_create_campaign_count;idx++){
5367 // if we found the item
5368 if(select_index < 0){
5377 int multi_create_ok_to_commit()
5379 int player_count, observer_count, idx;
5380 int notify_of_hacked_ships_tbl = 0;
5381 int notify_of_hacked_weapons_tbl = 0;
5382 char err_string[255];
5386 // make sure we have a valid mission selected
5387 if(Multi_create_list_select < 0){
5391 // if this is not a valid mission, let the player know
5392 abs_index = multi_create_select_to_index(Multi_create_list_select);
5397 // if we're playing with a hacked ships.tbl (on PXO)
5398 notify_of_hacked_ships_tbl = 0;
5399 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5400 if(!Game_ships_tbl_valid){
5401 notify_of_hacked_ships_tbl = 1;
5404 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5405 notify_of_hacked_ships_tbl = 1;
5408 if(!MULTI_IS_TRACKER_GAME){
5409 notify_of_hacked_ships_tbl = 0;
5411 if(notify_of_hacked_ships_tbl){
5412 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You or the server you are playing on has a hacked ships.tbl. Your stats will not be updated on PXO", 1051)) <= 0){
5417 // if we're playing with a hacked weapons.tbl (on PXO)
5418 notify_of_hacked_weapons_tbl = 0;
5419 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5420 if(!Game_weapons_tbl_valid){
5421 notify_of_hacked_weapons_tbl = 1;
5424 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5425 notify_of_hacked_weapons_tbl = 1;
5428 if(!MULTI_IS_TRACKER_GAME){
5429 notify_of_hacked_weapons_tbl = 0;
5431 if(notify_of_hacked_weapons_tbl){
5432 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You or the server you are playing on has a hacked weapons.tbl. Your stats will not be updated on PXO", 1052)) <= 0){
5437 // if any of the players have hacked data
5439 for(idx=0; idx<MAX_PLAYERS; idx++){
5440 // look for hacked players
5441 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5445 // message everyone - haha
5446 if(Net_players[idx].player != NULL){
5447 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5449 sprintf(err_string, "%s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5451 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5454 // if we found a hacked set of data
5457 if(MULTI_IS_TRACKER_GAME){
5458 // don't allow squad war matches to continue
5459 if(Netgame.type_flags & NG_TYPE_SW){
5461 // if this is squad war, don't allow it to continue
5462 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("One or more players has hacked data files. You cannot play a SquadWar match unless all clients have legal data", 1272));
5467 // otherwise, warn the players that stats will not saved
5469 // if this is squad war, don't allow it to continue
5470 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("One or more players has hacked data files. If you continue, stats will not be stored at the end of the mission", 1273)) <= 0){
5475 // non-pxo, just give a notice
5477 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("One or more players has hacked data files", 1274)) <= 0){
5483 // check to see that we don't have too many observers
5484 observer_count = multi_num_observers();
5485 if(observer_count > Netgame.options.max_observers){
5486 // print up the error string
5487 sprintf(err_string,XSTR("There are too many observers in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few",791),Netgame.options.max_observers,observer_count);
5489 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5493 // check to see that we have a valid # of players for the the # of ships in the game
5494 player_count = multi_num_players();
5495 if(player_count > Netgame.max_players){
5496 // print up the error string
5497 sprintf(err_string,XSTR("There are too many players in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few", 792), Netgame.max_players,player_count);
5499 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5503 // check to see if teams are assigned properly in a team vs. team situation
5504 if(Netgame.type_flags & NG_TYPE_TEAM){
5505 if(!multi_team_ok_to_commit()){
5506 gamesnd_play_iface(SND_GENERAL_FAIL);
5507 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5513 if(!multi_create_verify_cds()){
5514 gamesnd_play_iface(SND_GENERAL_FAIL);
5516 #ifdef MULTIPLAYER_BETA_BUILD
5517 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5519 #ifdef DVD_MESSAGE_HACK
5520 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5522 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5528 // if we're playing on the tracker
5529 if(MULTI_IS_TRACKER_GAME){
5530 #ifdef PXO_CHECK_VALID_MISSIONS
5531 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5532 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You have selected a mission which is either invalid or unknown to PXO. Your stats will not be saved if you continue",996)) <= 0){
5539 if(!(Netgame.type_flags & NG_TYPE_SW)){
5540 // if he is playing by himself, tell him stats will not be accepted
5541 if(multi_num_players() == 1){
5542 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("Warning\n\nIf you start a PXO mission by yourself, your stats will not be updated", 997)) <= 0){
5555 int multi_create_verify_cds()
5557 int player_count = multi_num_players();
5561 // count how many cds we have
5563 for(idx=0;idx<MAX_PLAYERS;idx++){
5564 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5569 // for the beta, everyone must have a CD
5570 #ifdef MULTIPLAYER_BETA_BUILD
5571 if(multi_cd_count < player_count){
5575 // determine if we have enough
5576 float ratio = (float)player_count / (float)multi_cd_count;
5577 // greater than a 4 to 1 ratio
5583 // we meet the conditions
5587 // returns an index into Multi_create_mission_list
5588 int multi_create_lookup_mission(char *fname)
5592 for(idx=0; idx<Multi_create_mission_count; idx++){
5593 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5598 // couldn't find the mission
5602 // returns an index into Multi_create_campaign_list
5603 int multi_create_lookup_campaign(char *fname)
5607 for(idx=0; idx<Multi_create_campaign_count; idx++){
5608 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5613 // couldn't find the campaign
5617 void multi_create_refresh_pxo()
5619 // delete mvalid.cfg if it exists
5620 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5622 // refresh missions from the tracker
5623 multi_update_valid_missions();
5626 void multi_create_sw_clicked()
5628 netgame_info ng_temp;
5631 int file_index = multi_create_select_to_index(Multi_create_list_select);
5633 // either a temporary netgame or the real one
5634 if(MULTIPLAYER_MASTER){
5641 // maybe switch squad war off
5642 if(!Multi_create_sw_checkbox.checked()){
5643 // if the mission selected is a coop mission, go back to coop mode
5644 Assert(file_index != -1);
5645 if(file_index == -1){
5646 ng->type_flags = NG_TYPE_COOP;
5648 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5649 ng->type_flags = NG_TYPE_TVT;
5650 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5651 ng->type_flags = NG_TYPE_DOGFIGHT;
5653 ng->type_flags = NG_TYPE_COOP;
5656 // switch squad war on
5658 Assert(file_index != -1);
5659 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5660 Multi_create_sw_checkbox.set_state(0);
5662 // at this point we know its safe to switch squad war on
5663 ng->type_flags = NG_TYPE_SW;
5668 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5670 send_netgame_update_packet();
5672 // update all machines about stuff like respawns, etc.
5673 multi_options_update_netgame();
5675 // on the standalone
5677 // standalone will take care of polling the usertracker
5678 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5683 // -------------------------------------------------------------------------------------------------------------
5685 // MULTIPLAYER HOST OPTIONS SCREEN
5688 #define MULTI_HO_NUM_BUTTONS 12
5689 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5693 #define MULTI_HO_PALETTE "InterfacePalette"
5695 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5696 "MultiHost", // GR_640
5697 "2_MultiHost" // GR_1024
5700 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5701 "MultiHost-M", // GR_640
5702 "2_MultiHost-M" // GR_1024
5706 UI_WINDOW Multi_ho_window; // the window object for the join screen
5707 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5708 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5709 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5710 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5711 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5712 int Multi_ho_bitmap; // the background bitmap
5714 // constants for coordinate lookup
5715 #define MULTI_HO_X_COORD 0
5716 #define MULTI_HO_Y_COORD 1
5717 #define MULTI_HO_W_COORD 2
5718 #define MULTI_HO_H_COORD 3
5719 #define MULTI_HO_TEXT_X_COORD 4
5720 #define MULTI_HO_TEXT_Y_COORD 5
5723 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5724 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5725 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5726 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5727 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5728 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5729 #define MULTI_HO_END_ANY 6 // any player can end the mission
5730 #define MULTI_HO_END_HOST 7 // only host can end the mission
5731 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5732 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5733 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5734 #define MULTI_HO_ACCEPT 11 // accept button
5736 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5738 // who is allowed to message
5739 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
5740 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
5741 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
5742 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
5744 // who is allowed to end the mission
5745 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
5746 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
5747 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
5748 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
5750 // voice on/off button
5751 ui_button_info("MH_09", 542, 158, 545, 185, 9),
5752 ui_button_info("MH_10", 598, 158, 604, 185, 10),
5754 // host modifies ships
5755 ui_button_info("MH_13", 542, 377, 437, 363, 13),
5758 ui_button_info("MH_14", 572, 428, 580, 414, 14),
5761 // who is allowed to message
5762 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
5763 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
5764 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
5765 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
5767 // who is allowed to end the mission
5768 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
5769 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
5770 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
5771 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
5773 // voice on/off button
5774 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
5775 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
5777 // host modifies ships
5778 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
5781 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
5784 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5786 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
5787 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
5788 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
5789 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
5790 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
5791 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
5792 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
5793 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
5794 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
5795 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
5796 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
5797 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
5800 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
5801 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
5802 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
5803 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
5804 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
5805 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
5806 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
5807 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
5808 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
5809 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
5810 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
5811 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
5815 // radio button controls
5816 #define MULTI_HO_NUM_RADIO_GROUPS 3
5817 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
5818 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
5819 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
5820 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
5823 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
5824 // { group #, value, button id# }
5825 {0, 0, 0}, // highest ranking players can do messaging
5826 {0, 1, 1}, // wing/team leaders can do messaging
5827 {0, 2, 2}, // any player can do messaging
5828 {0, 3, 3}, // only host can do messaging
5829 {1, 0, 4}, // highest rank and host can end the mission
5830 {1, 1, 5}, // team/wing leader can end the mission
5831 {1, 2, 6}, // any player can end the mission
5832 {1, 3, 7}, // only the host can end the mission
5833 {2, 0, 8}, // voice toggled on
5834 {2, 1, 9} // voice toggled off
5838 #define MULTI_HO_NUM_SLIDERS 3
5839 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
5840 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
5841 #define MULTI_HO_SLIDER_SKILL 2 // skill level
5848 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
5850 ho_sliders(char *name, int x1, int y1, int xt1, int yt1, int h, int _dot_w, int _dots) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), dot_w(_dot_w), dots(_dots){}
5852 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
5854 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
5855 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
5856 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
5859 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
5860 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
5861 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
5865 int Multi_ho_mission_respawn;
5867 int Multi_ho_host_modifies;
5869 // whether or not any of the inputboxes on this screen had focus last frame
5870 int Multi_ho_lastframe_input = 0;
5872 // game information text areas
5875 #define MULTI_HO_NUM_TITLES 14
5876 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
5878 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5879 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
5880 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
5881 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
5882 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
5883 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
5884 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
5885 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
5886 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5887 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
5888 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
5889 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
5890 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
5891 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
5894 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
5895 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
5896 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
5897 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
5898 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
5899 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
5900 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
5901 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
5902 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
5903 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
5904 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
5905 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
5906 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
5907 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
5911 // mission time limit input box
5912 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
5921 // furball kill limit input box
5922 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
5931 // voice recording duration text display area
5932 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
5941 // voice token wait input box
5942 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
5951 // observer count input box
5952 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
5961 // skill text description area
5962 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
5971 // respawn input box
5972 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
5981 // respawn max text area
5982 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
5991 // maximum values for various input boxes (to notify user of overruns)
5992 #define MULTI_HO_MAX_TIME_LIMIT 500
5993 #define MULTI_HO_MAX_TOKEN_WAIT 5
5994 #define MULTI_HO_MAX_KILL_LIMIT 9999
5995 #define MULTI_HO_MAX_OBS 4
5997 // LOCAL function definitions
5998 void multi_ho_check_buttons();
5999 void multi_ho_button_pressed(int n);
6000 void multi_ho_draw_radio_groups();
6001 void multi_ho_accept_hit();
6002 void multi_ho_get_options();
6003 void multi_ho_apply_options();
6004 void multi_ho_display_record_time();
6005 int multi_ho_check_values();
6006 void multi_ho_check_focus();
6007 void multi_ho_blit_max_respawns();
6008 void multi_ho_display_skill_level();
6010 void multi_host_options_init()
6014 // create the interface window
6015 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6016 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6018 // load the background bitmap
6019 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6020 if(Multi_ho_bitmap < 0){
6021 // we failed to load the bitmap - this is very bad
6025 // initialize the common notification messaging
6026 multi_common_notify_init();
6028 // use the common interface palette
6029 multi_common_set_palette();
6031 // create the interface buttons
6032 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6033 // create the object
6034 Multi_ho_buttons[gr_screen.res][idx].button.create(&Multi_ho_window, "", Multi_ho_buttons[gr_screen.res][idx].x, Multi_ho_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
6036 // set the sound to play when highlighted
6037 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6039 // set the ani for the button
6040 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6042 // set the hotspot, ignoring the skill level button
6043 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6046 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6050 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6051 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6054 // create the interface sliders
6055 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6056 // create the object
6057 Multi_ho_sliders[gr_screen.res][idx].slider.create(&Multi_ho_window, Multi_ho_sliders[gr_screen.res][idx].x, Multi_ho_sliders[gr_screen.res][idx].y, Multi_ho_sliders[gr_screen.res][idx].dots, Multi_ho_sliders[gr_screen.res][idx].filename, Multi_ho_sliders[gr_screen.res][idx].hotspot, NULL, -1, -1, -1, NULL, -1, -1, -1, Multi_ho_sliders[gr_screen.res][idx].dot_w);
6060 // create the respawn count input box
6061 Multi_ho_respawns.create(&Multi_ho_window,Ho_rsp_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_rsp_coords[gr_screen.res][MULTI_HO_Y_COORD],Ho_rsp_coords[gr_screen.res][MULTI_HO_W_COORD],6,"",UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS,-1,&Color_bright);
6062 // if we're in campaign mode, disable it
6063 if(Netgame.campaign_mode == MP_CAMPAIGN){
6064 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6065 Multi_ho_respawns.disable();
6067 Multi_ho_respawns.set_valid_chars("0123456789");
6070 // create the time limit input box
6071 Multi_ho_time_limit.create(&Multi_ho_window, Ho_time_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_time_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_time_coords[gr_screen.res][MULTI_HO_W_COORD], 3, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6072 Multi_ho_time_limit.set_valid_chars("-0123456789");
6074 // create the voice token wait input box
6075 Multi_ho_voice_wait.create(&Multi_ho_window, Ho_vw_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_vw_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_vw_coords[gr_screen.res][MULTI_HO_W_COORD], 1, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6076 Multi_ho_voice_wait.set_valid_chars("01243456789");
6078 // create the furball kill limit input box
6079 Multi_ho_kill_limit.create(&Multi_ho_window, Ho_kill_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_kill_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_kill_coords[gr_screen.res][MULTI_HO_W_COORD], 4, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6080 Multi_ho_kill_limit.set_valid_chars("0123456789");
6082 // create the observer limit input box
6083 Multi_ho_obs.create(&Multi_ho_window, Ho_obs_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_obs_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_obs_coords[gr_screen.res][MULTI_HO_W_COORD], 1, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6084 Multi_ho_obs.set_valid_chars("01234");
6086 // load in the current netgame defaults
6087 multi_ho_get_options();
6089 // whether or not any of the inputboxes on this screen had focus last frame
6090 Multi_ho_lastframe_input = 0;
6092 // get the # of respawns for the currently selected mission (if any)
6093 if(Multi_create_list_select != -1){
6094 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6096 // if he has a valid mission selected
6098 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6100 Multi_ho_mission_respawn = -1;
6103 Multi_ho_mission_respawn = -1;
6107 void multi_ho_update_sliders()
6109 // game skill slider
6110 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6111 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6112 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6113 gamesnd_play_iface(SND_USER_SELECT);
6115 Game_skill_level = NUM_SKILL_LEVELS / 2;
6119 // get the voice qos options
6120 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6121 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6122 gamesnd_play_iface(SND_USER_SELECT);
6125 // get the voice duration options
6126 if (Netgame.options.voice_record_time != (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f)) {
6127 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6128 gamesnd_play_iface(SND_USER_SELECT);
6133 void multi_host_options_do()
6138 k = Multi_ho_window.process();
6141 // process any keypresses
6144 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6147 case KEY_CTRLED + KEY_ENTER :
6148 gamesnd_play_iface(SND_COMMIT_PRESSED);
6149 multi_ho_accept_hit();
6153 // process any button clicks
6154 multi_ho_check_buttons();
6156 // update the sliders
6157 multi_ho_update_sliders();
6159 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6160 multi_ho_check_focus();
6162 // draw the background, etc
6164 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6165 if(Multi_ho_bitmap != -1){
6166 gr_set_bitmap(Multi_ho_bitmap);
6169 Multi_ho_window.draw();
6171 // draw all the radio buttons properly
6172 multi_ho_draw_radio_groups();
6174 // display any pending notification messages
6175 multi_common_notify_do();
6177 // display the voice record time settings
6178 multi_ho_display_record_time();
6180 // maybe display the max # of respawns next to the respawn input box
6181 multi_ho_blit_max_respawns();
6183 // blit the proper skill level
6184 multi_ho_display_skill_level();
6186 // blit the "host modifies button"
6187 if(Multi_ho_host_modifies){
6188 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6191 // process and show the chatbox thingie
6195 Multi_ho_window.draw_tooltip();
6197 // display the voice status indicator
6198 multi_common_voice_display_status();
6204 void multi_host_options_close()
6206 // unload any bitmaps
6207 if(!bm_unload(Multi_ho_bitmap)){
6208 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6211 // destroy the UI_WINDOW
6212 Multi_ho_window.destroy();
6215 void multi_ho_check_buttons()
6218 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6219 // we only really need to check for one button pressed at a time, so we can break after
6221 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6222 multi_ho_button_pressed(idx);
6228 void multi_ho_button_pressed(int n)
6230 int radio_index,idx;
6231 int x_pixel,y_pixel;
6233 // get the pixel position of the click
6234 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6237 // clicked on the accept button
6238 case MULTI_HO_ACCEPT:
6239 gamesnd_play_iface(SND_COMMIT_PRESSED);
6240 multi_ho_accept_hit();
6243 // clicked on the host/captains only modify button
6244 case MULTI_HO_HOST_MODIFIES:
6245 // toggle it on or off
6246 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6247 gamesnd_play_iface(SND_USER_SELECT);
6251 // look through the radio buttons and see which one this corresponds to
6253 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6254 if(Multi_ho_radio_info[idx][2] == n){
6259 Assert(radio_index != -1);
6261 // check to see if a radio button was pressed
6262 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6263 // see if this value is already picked for this radio group
6264 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6265 gamesnd_play_iface(SND_USER_SELECT);
6266 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6268 gamesnd_play_iface(SND_GENERAL_FAIL);
6273 void multi_ho_draw_radio_groups()
6277 // go through each item and draw it if it is the selected button in its respective group
6278 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6279 /// if this button is the currently selected one in its group
6280 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6281 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6286 void multi_ho_accept_hit()
6290 // check the values in the input boxes
6291 if(!multi_ho_check_values()){
6295 // zero out the netgame flags
6298 // set default options
6299 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6301 // set the squadmate messaging flags
6302 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6304 Netgame.options.squad_set = MSO_SQUAD_RANK;
6307 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6310 Netgame.options.squad_set = MSO_SQUAD_ANY;
6313 Netgame.options.squad_set = MSO_SQUAD_HOST;
6319 // set the end mission flags
6320 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6322 Netgame.options.endgame_set = MSO_END_RANK;
6325 Netgame.options.endgame_set = MSO_END_LEADER;
6328 Netgame.options.endgame_set = MSO_END_ANY;
6331 Netgame.options.endgame_set = MSO_END_HOST;
6337 // set the voice toggle
6338 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6340 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6343 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6349 // get the voice qos options
6350 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6352 // get the voice duration options
6353 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6355 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6356 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6357 // written in a bunch of locations....sigh.
6358 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6359 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6361 Game_skill_level = NUM_SKILL_LEVELS / 2;
6364 // set the netgame respawn count
6365 // maybe warn the user that respawns will not be used for a campaign mission
6366 if(Netgame.campaign_mode == MP_SINGLE){
6367 Multi_ho_respawns.get_text(resp_str);
6368 uint temp_respawn = (uint)atoi(resp_str);
6369 // if he currently has no mission selected, let the user set any # of respawns
6370 if((int)temp_respawn > Multi_ho_mission_respawn){
6371 if(Multi_ho_mission_respawn == -1){
6372 Netgame.respawn = temp_respawn;
6373 Netgame.options.respawn = temp_respawn;
6375 // this should have been taken care of by the interface code
6380 Netgame.options.respawn = temp_respawn;
6381 Netgame.respawn = temp_respawn;
6385 // get the mission time limit
6386 Multi_ho_time_limit.get_text(resp_str);
6387 int temp_time = atoi(resp_str);
6389 Netgame.options.mission_time_limit = fl2f(-1.0f);
6390 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6393 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6396 // get observer count options
6397 Multi_ho_obs.get_text(resp_str);
6398 int temp_obs = atoi(resp_str);
6399 if(temp_obs > MULTI_HO_MAX_OBS){
6402 Netgame.options.max_observers = (ubyte)temp_obs;
6404 // get the furball kill limit
6405 Multi_ho_kill_limit.get_text(resp_str);
6406 int temp_kills = atoi(resp_str);
6407 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6410 Netgame.options.kill_limit = temp_kills;
6412 // get the token wait limit
6413 Multi_ho_voice_wait.get_text(resp_str);
6414 int temp_wait = atoi(resp_str);
6415 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6418 Netgame.options.voice_token_wait = (temp_wait * 1000);
6420 // set the netgame option
6421 Netgame.options.skill_level = (ubyte)Game_skill_level;
6423 // get whether we're in host/captains only modify mode
6424 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6425 if(Multi_ho_host_modifies){
6426 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6429 // store these values locally
6430 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6431 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6432 write_pilot_file(Player);
6434 // apply any changes in settings (notify everyone of voice qos changes, etc)
6435 multi_ho_apply_options();
6437 // move back to the create game screen
6438 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6441 void multi_ho_get_options()
6445 // set the squadmate messaging buttons
6446 switch(Netgame.options.squad_set){
6447 case MSO_SQUAD_RANK :
6448 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6450 case MSO_SQUAD_LEADER:
6451 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6454 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6456 case MSO_SQUAD_HOST:
6457 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6463 // set the mission end buttons
6464 switch(Netgame.options.endgame_set){
6466 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6468 case MSO_END_LEADER:
6469 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6472 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6475 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6481 // set the voice toggle buttons
6482 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6483 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6485 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6488 // get the voice qos options
6489 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6490 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6492 // get the voice duration options
6493 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6494 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6496 // get the current skill level
6497 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6498 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6500 // get the # of observers
6501 memset(resp_str,0,10);
6502 sprintf(resp_str,"%d",Netgame.options.max_observers);
6503 Multi_ho_obs.set_text(resp_str);
6505 // set the respawn count
6506 if(Netgame.campaign_mode == MP_SINGLE){
6507 memset(resp_str,0,10);
6508 sprintf(resp_str,"%d",Netgame.respawn);
6509 Multi_ho_respawns.set_text(resp_str);
6512 // set the mission time limit
6513 memset(resp_str,0,10);
6514 float tl = f2fl(Netgame.options.mission_time_limit);
6515 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6516 Multi_ho_time_limit.set_text(resp_str);
6518 // set the furball kill limit
6519 memset(resp_str,0,10);
6520 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6521 Multi_ho_kill_limit.set_text(resp_str);
6523 // set the token wait time
6524 memset(resp_str,0,10);
6525 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6526 Multi_ho_voice_wait.set_text(resp_str);
6528 // get whether we're in host/captains only modify mode
6529 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6530 Multi_ho_host_modifies = 1;
6532 Multi_ho_host_modifies = 0;
6536 void multi_ho_apply_options()
6538 // if the voice qos or duration has changed, apply the change
6539 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6541 // send an options update
6542 multi_options_update_netgame();
6545 // display the voice record time settings
6546 void multi_ho_display_record_time()
6549 int full_seconds, half_seconds;
6552 memset(time_str,0,30);
6555 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6557 // get the half-seconds
6558 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6560 // format the string
6561 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6562 gr_set_color_fast(&Color_bright);
6563 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6566 int multi_ho_check_values()
6570 memset(val_txt,0,255);
6572 // check against respawn settings
6573 if(Multi_ho_mission_respawn != -1){
6574 Multi_ho_respawns.get_text(val_txt);
6575 // if the value is invalid, let the user know
6576 if(atoi(val_txt) > Multi_ho_mission_respawn){
6577 memset(val_txt,0,255);
6578 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6579 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6584 // check against mission time limit max
6585 Multi_ho_time_limit.get_text(val_txt);
6586 // if the value is invalid, force it to be valid
6587 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6588 memset(val_txt,0,255);
6589 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6590 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6594 // check against max observer limit
6595 Multi_ho_obs.get_text(val_txt);
6596 // if the value is invalid, force it to be valid
6597 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6598 memset(val_txt,0,255);
6599 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6600 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6604 // check against furball kill limit
6605 Multi_ho_kill_limit.get_text(val_txt);
6606 // if the value is invalid, force it to be valid
6607 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6608 memset(val_txt,0,255);
6609 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6610 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6614 // check against the token wait limit
6615 Multi_ho_voice_wait.get_text(val_txt);
6616 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6617 memset(val_txt,0,255);
6618 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6619 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6623 // all values are valid
6627 void multi_ho_check_focus()
6629 // if an inputbox has been pressed (hit enter), lose its focus
6630 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6631 Multi_ho_respawns.clear_focus();
6632 Multi_ho_time_limit.clear_focus();
6633 Multi_ho_voice_wait.clear_focus();
6634 Multi_ho_kill_limit.clear_focus();
6635 Multi_ho_obs.clear_focus();
6636 gamesnd_play_iface(SND_COMMIT_PRESSED);
6637 chatbox_set_focus();
6638 Multi_ho_lastframe_input = 0;
6640 } else if(!Multi_ho_lastframe_input) {
6641 // if we didn't have focus last frame
6642 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6643 chatbox_lose_focus();
6645 Multi_ho_lastframe_input = 1;
6648 // if we _did_ have focus last frame
6650 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6651 if(!Multi_ho_respawns.has_focus() && !Multi_ho_time_limit.has_focus() && !Multi_ho_kill_limit.has_focus() && !Multi_ho_voice_wait.has_focus() && !chatbox_has_focus()){
6652 chatbox_set_focus();
6654 // if the chatbox now has focus, clear all focus from our inputboxes
6655 else if (chatbox_has_focus()) {
6656 Multi_ho_respawns.clear_focus();
6657 Multi_ho_time_limit.clear_focus();
6658 Multi_ho_kill_limit.clear_focus();
6659 Multi_ho_voice_wait.clear_focus();
6661 Multi_ho_lastframe_input = 0;
6666 void multi_ho_blit_max_respawns()
6670 // if we're in campaign mode, do nothing
6671 if(Netgame.campaign_mode == MP_CAMPAIGN){
6675 // otherwise blit the max as specified by the current mission file
6676 sprintf(string,"(%d)",Multi_ho_mission_respawn);
6677 gr_set_color_fast(&Color_normal);
6678 gr_string(Ho_max_rsp_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_max_rsp_coords[gr_screen.res][MULTI_HO_Y_COORD], string);
6681 void multi_ho_display_skill_level()
6683 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6686 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
6687 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
6691 gr_set_color_fast(&Color_bright);
6692 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
6695 // -------------------------------------------------------------------------------------------------------------
6697 // MULTIPLAYER JOIN SCREEN
6700 #define MULTI_JW_NUM_BUTTONS 8
6704 #define MULTI_JW_PALETTE "InterfacePalette"
6706 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
6707 "MultiJoinWait", // GR_640
6708 "2_MultiJoinWait" // GR_1024
6711 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
6712 "MultiJoinWait-M", // GR_640
6713 "2_MultiJoinWait-M" // GR_1024
6719 #define MJW_SCROLL_PLAYERS_UP 0
6720 #define MJW_SCROLL_PLAYERS_DOWN 1
6723 #define MJW_PILOT_INFO 4
6724 #define MJW_SCROLL_INFO_UP 5
6725 #define MJW_SCROLL_INFO_DOWN 6
6726 #define MJW_CANCEL 7
6728 UI_WINDOW Multi_jw_window; // the window object for the join screen
6729 int Multi_jw_bitmap; // the background bitmap
6731 // constants for coordinate lookup
6732 #define MJW_X_COORD 0
6733 #define MJW_Y_COORD 1
6734 #define MJW_W_COORD 2
6735 #define MJW_H_COORD 3
6737 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
6739 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
6740 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
6741 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
6742 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
6743 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
6744 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
6745 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
6746 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
6749 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
6750 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
6751 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
6752 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
6753 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
6754 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
6755 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
6756 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
6760 #define MULTI_JW_NUM_TEXT 7
6762 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
6764 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
6765 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
6766 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6767 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6768 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
6769 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
6770 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
6773 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
6774 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
6775 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6776 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6777 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
6778 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
6779 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
6783 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
6792 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
6801 // squad war checkbox
6802 UI_CHECKBOX Multi_jw_sw_checkbox;
6803 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
6807 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
6815 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
6825 // player list control thingie defs
6826 #define MULTI_JW_PLIST_MAX_DISPLAY 19
6827 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
6828 short Multi_jw_plist_select_id; // id of the current selected player
6829 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
6831 int Multi_jw_should_show_popup = 0;
6833 // LOCAL function definitions
6834 void multi_jw_check_buttons();
6835 void multi_jw_button_pressed(int n);
6836 void multi_jw_do_netstuff();
6837 void multi_jw_scroll_players_up();
6838 void multi_jw_scroll_players_down();
6839 void multi_jw_plist_process();
6840 void multi_jw_plist_blit_normal();
6841 void multi_jw_plist_blit_team();
6842 short multi_jw_get_mouse_id();
6844 void multi_game_client_setup_init()
6848 // create the interface window
6849 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6850 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
6852 // load the background bitmap
6853 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
6854 if(Multi_jw_bitmap < 0){
6855 // we failed to load the bitmap - this is very bad
6859 // initialize the player list data
6860 Multi_jw_plist_select_flag = 0;
6861 Multi_jw_plist_select_id = -1;
6863 // kill any old instances of the chatbox and create a new one
6865 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
6867 // initialize the common notification messaging
6868 multi_common_notify_init();
6870 // initialize the common mission info display area.
6871 multi_common_set_text("");
6873 // use the common interface palette
6874 multi_common_set_palette();
6876 // create the interface buttons
6877 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
6878 // create the object
6879 Multi_jw_buttons[gr_screen.res][idx].button.create(&Multi_jw_window, "", Multi_jw_buttons[gr_screen.res][idx].x, Multi_jw_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
6881 // set the sound to play when highlighted
6882 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6884 // set the ani for the button
6885 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
6888 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
6891 // if this is a PXO game, enable the squadwar checkbox
6892 Multi_jw_sw_checkbox.create(&Multi_jw_window, "", Multi_jw_sw_checkbox_coords[gr_screen.res][0], Multi_jw_sw_checkbox_coords[gr_screen.res][1], 0);
6893 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
6894 Multi_jw_sw_checkbox.disable();
6895 if(!MULTI_IS_TRACKER_GAME){
6896 Multi_jw_sw_checkbox.hide();
6900 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
6901 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
6904 // create the player select list button and hide it
6905 Multi_jw_plist_select_button.create(&Multi_jw_window, "", Mjw_players_coords[gr_screen.res][MJW_X_COORD], Mjw_players_coords[gr_screen.res][MJW_Y_COORD], Mjw_players_coords[gr_screen.res][MJW_W_COORD], Mjw_players_coords[gr_screen.res][MJW_H_COORD], 0, 1);
6906 Multi_jw_plist_select_button.hide();
6909 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
6911 // remove campaign flags
6912 Game_mode &= ~(GM_CAMPAIGN_MODE);
6914 // tell the server we have finished joining
6915 Net_player->state = NETPLAYER_STATE_JOINED;
6916 send_netplayer_update_packet();
6919 ml_printf(NOX("Joined netgame %s"), Netgame.name);
6921 // send any appropriate files
6922 multi_data_send_my_junk();
6925 void multi_game_client_setup_do_frame()
6928 int k = chatbox_process();
6929 char mission_text[255];
6930 k = Multi_jw_window.process(k,0);
6932 Multi_jw_should_show_popup = 0;
6934 // process any button clicks
6935 multi_jw_check_buttons();
6937 // do any network related stuff
6938 multi_jw_do_netstuff();
6940 // draw the background, etc
6942 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
6943 if(Multi_jw_bitmap != -1){
6944 gr_set_bitmap(Multi_jw_bitmap);
6948 // if we're not in team vs. team mode, don't draw the team buttons
6949 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
6950 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
6951 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
6952 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
6953 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
6955 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
6956 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
6957 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
6958 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
6961 if(MULTI_IS_TRACKER_GAME){
6962 // maybe check the squadwar button
6963 if(Netgame.type_flags & NG_TYPE_SW){
6964 Multi_jw_sw_checkbox.set_state(1);
6965 gr_set_color_fast(&Color_bright);
6967 Multi_jw_sw_checkbox.set_state(0);
6968 gr_set_color_fast(&Color_normal);
6971 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
6974 // draw the UI window
6975 Multi_jw_window.draw();
6977 // process and display the player list
6978 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
6979 multi_jw_plist_process();
6980 if(Netgame.type_flags & NG_TYPE_TEAM){
6981 multi_jw_plist_blit_team();
6983 multi_jw_plist_blit_normal();
6986 // display any text in the info area
6987 multi_common_render_text();
6989 // display any pending notification messages
6990 multi_common_notify_do();
6992 // blit the mission filename if possible
6993 if(Netgame.campaign_mode){
6994 if(strlen(Netgame.campaign_name) > 0){
6995 strcpy(mission_text,Netgame.campaign_name);
6997 if(strlen(Netgame.title) > 0){
6998 strcat(mission_text,", ");
6999 strcat(mission_text,Netgame.title);
7002 gr_set_color_fast(&Color_bright_white);
7003 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7006 if(strlen(Netgame.mission_name) > 0){
7007 strcpy(mission_text,Netgame.mission_name);
7009 if(strlen(Netgame.title) > 0){
7010 strcat(mission_text,", ");
7011 strcat(mission_text,Netgame.title);
7014 gr_set_color_fast(&Color_bright_white);
7015 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7019 // process and show the chatbox thingie
7023 Multi_jw_window.draw_tooltip();
7025 // display the voice status indicator
7026 multi_common_voice_display_status();
7031 // if we're supposed to be displaying a pilot info popup
7032 if(Multi_jw_should_show_popup){
7033 player_index = find_player_id(Multi_jw_plist_select_id);
7034 if(player_index != -1){
7035 multi_pinfo_popup(&Net_players[player_index]);
7040 void multi_game_client_setup_close()
7042 // unload any bitmaps
7043 if(!bm_unload(Multi_jw_bitmap)){
7044 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7047 // destroy the chatbox
7050 // destroy the UI_WINDOW
7051 Multi_jw_window.destroy();
7054 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7055 gamesnd_play_iface(SND_COMMIT_PRESSED);
7060 void multi_jw_check_buttons()
7063 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7064 // we only really need to check for one button pressed at a time, so we can break after
7066 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7067 multi_jw_button_pressed(idx);
7073 void multi_jw_button_pressed(int n)
7077 gamesnd_play_iface(SND_USER_SELECT);
7078 multi_quit_game(PROMPT_CLIENT);
7080 case MJW_SCROLL_PLAYERS_UP:
7081 multi_jw_scroll_players_up();
7083 case MJW_SCROLL_PLAYERS_DOWN:
7084 multi_jw_scroll_players_down();
7086 case MJW_SCROLL_INFO_UP:
7087 multi_common_scroll_text_up();
7089 case MJW_SCROLL_INFO_DOWN:
7090 multi_common_scroll_text_down();
7093 // request to set myself to team 0
7095 gamesnd_play_iface(SND_USER_SELECT);
7096 multi_team_set_team(Net_player,0);
7099 // request to set myself to team 1
7101 gamesnd_play_iface(SND_USER_SELECT);
7102 multi_team_set_team(Net_player,1);
7106 case MJW_PILOT_INFO:
7107 Multi_jw_should_show_popup = 1;
7111 multi_common_add_notify(XSTR("Not implemented yet!",760));
7116 // do stuff like pinging servers, sending out requests, etc
7117 void multi_jw_do_netstuff()
7121 void multi_jw_scroll_players_up()
7123 gamesnd_play_iface(SND_GENERAL_FAIL);
7126 // scroll down through the player list
7127 void multi_jw_scroll_players_down()
7129 gamesnd_play_iface(SND_GENERAL_FAIL);
7132 void multi_jw_plist_process()
7134 int test_count,player_index,idx;
7136 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7138 for(idx=0;idx<MAX_PLAYERS;idx++){
7139 // count anyone except the standalone server (if applicable)
7140 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7144 if(test_count <= 0){
7148 // if we had a selected item but that player has left, select myself instead
7149 if(Multi_jw_plist_select_flag){
7150 player_index = find_player_id(Multi_jw_plist_select_id);
7151 if(player_index == -1){
7152 Multi_jw_plist_select_id = Net_player->player_id;
7155 Multi_jw_plist_select_flag = 1;
7156 Multi_jw_plist_select_id = Net_player->player_id;
7159 // if the player has clicked somewhere in the player list area
7160 if(Multi_jw_plist_select_button.pressed()){
7164 player_id = multi_jw_get_mouse_id();
7165 player_index = find_player_id(player_id);
7166 if(player_index != -1){
7167 Multi_jw_plist_select_id = player_id;
7168 Multi_jw_plist_select_flag = 1;
7173 void multi_jw_plist_blit_normal()
7176 char str[CALLSIGN_LEN+1];
7177 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7180 // display all the players
7181 for(idx=0;idx<MAX_PLAYERS;idx++){
7182 // reset total offset
7185 // count anyone except the standalone server (if applicable)
7186 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7187 // highlight him if he's the host
7188 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7189 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7190 gr_set_color_fast(&Color_text_active_hi);
7192 gr_set_color_fast(&Color_bright);
7195 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7196 gr_set_color_fast(&Color_text_active);
7198 gr_set_color_fast(&Color_text_normal);
7202 // optionally draw his CD status
7203 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7204 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7205 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7207 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7210 // make sure the string will fit, then display it
7211 strcpy(str,Net_players[idx].player->callsign);
7212 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7215 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7216 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7223 void multi_jw_plist_blit_team()
7226 char str[CALLSIGN_LEN+1];
7227 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7230 // always blit the proper team button based on _my_ team status
7231 if(Net_player->p_info.team == 0){
7232 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7234 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7237 // display all the red players first
7238 for(idx=0;idx<MAX_PLAYERS;idx++){
7239 // reset total offset
7242 // count anyone except the standalone server (if applicable)
7243 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7244 // highlight him if he's the host
7245 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7246 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7247 gr_set_color_fast(&Color_text_active_hi);
7249 gr_set_color_fast(&Color_bright);
7252 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7253 gr_set_color_fast(&Color_text_active);
7255 gr_set_color_fast(&Color_text_normal);
7259 // optionally draw his CD status
7260 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7261 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7262 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7264 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7267 // blit the red team indicator
7268 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7269 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7270 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
7271 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7273 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7276 if(Multi_common_icons[MICON_TEAM0] != -1){
7277 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
7278 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7280 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7284 // make sure the string will fit
7285 strcpy(str,Net_players[idx].player->callsign);
7286 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7288 // display him in the correct half of the list depending on his team
7289 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7294 // display all the green players next
7295 for(idx=0;idx<MAX_PLAYERS;idx++){
7296 // reset total offset
7299 // count anyone except the standalone server (if applicable)
7300 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7301 // highlight him if he's the host
7302 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7303 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7304 gr_set_color_fast(&Color_text_active_hi);
7306 gr_set_color_fast(&Color_bright);
7309 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7310 gr_set_color_fast(&Color_text_active);
7312 gr_set_color_fast(&Color_text_normal);
7316 // optionally draw his CD status
7317 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7318 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7319 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7321 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7324 // blit the red team indicator
7325 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7326 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7327 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
7328 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7330 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7333 if(Multi_common_icons[MICON_TEAM1] != -1){
7334 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
7335 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7337 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7341 // make sure the string will fit
7342 strcpy(str,Net_players[idx].player->callsign);
7343 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7346 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7348 // display him in the correct half of the list depending on his team
7349 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7355 void multi_jw_handle_join(net_player *pl)
7357 // for now just play a bloop sound
7358 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7361 short multi_jw_get_mouse_id()
7363 // determine where he clicked (y pixel value)
7365 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7367 // select things a little differently if we're in team vs. team or non-team vs. team mode
7369 if(Netgame.type_flags & NG_TYPE_TEAM){
7370 int player_index = -1;
7372 // look through all of team red first
7373 for(idx=0;idx<MAX_PLAYERS;idx++){
7374 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7377 // if this is the _nth_ guy
7385 // if we still haven't found him yet, look through the green team
7386 if(player_index == -1){
7387 for(idx=0;idx<MAX_PLAYERS;idx++){
7388 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7390 // if this is the _nth_ guy
7398 if(player_index != -1){
7399 return Net_players[idx].player_id;
7402 // select the nth active player if possible, disregarding the standalone server
7403 for(idx=0;idx<MAX_PLAYERS;idx++){
7404 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7407 // if this is the _nth_ guy
7409 return Net_players[idx].player_id;
7420 // -------------------------------------------------------------------------------------------------------------
7422 // MULTIPLAYER WAIT/SYNCH SCREEN
7425 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7426 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7428 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7429 "MultiSynch", // GR_640
7430 "2_MultiSynch" // GR_1024
7433 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7434 "MultiSynch-M", // GR_640
7435 "2_MultiSynch-M" // GR_1024
7440 // constants for coordinate lookup
7441 #define MS_X_COORD 0
7442 #define MS_Y_COORD 1
7443 #define MS_W_COORD 2
7444 #define MS_H_COORD 3
7446 UI_WINDOW Multi_sync_window; // the window object for the join screen
7447 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7448 int Multi_sync_bitmap; // the background bitmap
7451 #define MULTI_SYNC_NUM_BUTTONS 5
7452 #define MS_SCROLL_INFO_UP 0
7453 #define MS_SCROLL_INFO_DOWN 1
7457 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7459 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7460 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7461 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7462 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7463 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7466 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7467 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7468 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7469 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7470 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7475 #define MULTI_SYNC_NUM_TEXT 5
7477 #define MST_LAUNCH 2
7478 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7480 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7481 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7482 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7483 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7484 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7487 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7488 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7489 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7490 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7491 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7496 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7505 // player status coords
7506 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7515 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7520 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7525 // player currently selected, index into Net_players[]
7526 int Multi_sync_player_select = -1;
7528 // player list control thingie defs
7529 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7530 int Multi_sync_plist_start; // where to start displaying from
7531 int Multi_sync_plist_count; // how many we have
7533 // list select button
7534 UI_BUTTON Multi_sync_plist_button;
7536 int Multi_sync_mode = -1;
7538 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7539 float Multi_sync_countdown_timer;
7540 int Multi_sync_countdown = -1;
7542 int Multi_launch_button_created;
7545 // countdown animation timer
7546 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7548 "2_Count" // GR_1024
7551 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7562 anim *Multi_sync_countdown_anim = NULL;
7563 anim_instance *Multi_sync_countdown_instance = NULL;
7566 // PREBRIEFING STUFF
7567 // syncing flags used by the server
7568 int Mission_sync_flags = 0;
7569 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7570 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7571 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7572 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7573 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7574 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7575 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7576 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7577 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7578 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7579 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7581 // POSTBRIEFING STUFF
7582 int Multi_state_timestamp;
7583 int Multi_sync_launch_pressed;
7585 // LOCAL function definitions
7586 void multi_sync_check_buttons();
7587 void multi_sync_button_pressed(int n);
7588 void multi_sync_scroll_info_up();
7589 void multi_sync_scroll_info_down();
7590 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7591 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7592 void multi_sync_force_start_pre();
7593 void multi_sync_force_start_post();
7594 void multi_sync_launch();
7595 void multi_sync_create_launch_button();
7596 void multi_sync_blit_screen_all();
7597 void multi_sync_handle_plist();
7599 void multi_sync_common_init();
7600 void multi_sync_common_do();
7601 void multi_sync_common_close();
7603 void multi_sync_pre_init();
7604 void multi_sync_pre_do();
7605 void multi_sync_pre_close();
7607 void multi_sync_post_init();
7608 void multi_sync_post_do();
7609 void multi_sync_post_close();
7614 // perform the correct init functions
7615 void multi_sync_init()
7617 Multi_sync_countdown = -1;
7621 // reset all timestamp
7622 multi_reset_timestamps();
7624 extern int Player_multi_died_check;
7625 Player_multi_died_check = -1;
7627 if(!(Game_mode & GM_STANDALONE_SERVER)){
7628 multi_sync_common_init();
7631 switch(Multi_sync_mode){
7632 case MULTI_SYNC_PRE_BRIEFING:
7633 multi_sync_pre_init();
7635 case MULTI_SYNC_POST_BRIEFING:
7636 multi_sync_post_init();
7638 case MULTI_SYNC_INGAME:
7639 multi_ingame_sync_init();
7644 // perform the correct do frame functions
7645 void multi_sync_do()
7647 if(!(Game_mode & GM_STANDALONE_SERVER)){
7648 multi_sync_common_do();
7651 // if the netgame is ending, don't do any sync processing
7652 if(multi_endgame_ending()){
7656 // process appropriateliy
7657 switch(Multi_sync_mode){
7658 case MULTI_SYNC_PRE_BRIEFING:
7659 multi_sync_pre_do();
7661 case MULTI_SYNC_POST_BRIEFING:
7662 multi_sync_post_do();
7664 case MULTI_SYNC_INGAME:
7665 multi_ingame_sync_do();
7668 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
7669 if(Multi_sync_bitmap != -1){
7670 gr_set_bitmap(Multi_sync_bitmap);
7673 Multi_sync_window.draw();
7675 multi_sync_blit_screen_all();
7682 // perform the correct close functions
7683 void multi_sync_close()
7685 switch(Multi_sync_mode){
7686 case MULTI_SYNC_PRE_BRIEFING:
7687 multi_sync_pre_close();
7689 case MULTI_SYNC_POST_BRIEFING:
7690 multi_sync_post_close();
7692 case MULTI_SYNC_INGAME:
7693 multi_ingame_sync_close();
7697 if(!(Game_mode & GM_STANDALONE_SERVER)){
7698 multi_sync_common_close();
7702 char *multi_sync_tooltip_handler(char *str)
7704 if (!stricmp(str, NOX("@launch"))) {
7705 if (Multi_launch_button_created){
7706 return XSTR("Launch",801);
7713 void multi_sync_common_init()
7717 // create the interface window
7718 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
7719 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
7720 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
7722 // load the background bitmap
7723 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
7724 if (Multi_sync_bitmap < 0) {
7725 // we failed to load the bitmap - this is very bad
7729 // initialize the player list data
7730 Multi_sync_plist_start = 0;
7731 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
7733 Multi_launch_button_created = 0;
7735 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
7738 // force the chatbox to be small
7739 chatbox_force_small();
7741 // initialize the common notification messaging
7742 multi_common_notify_init();
7744 // initialize the common mission info display area.
7745 multi_common_set_text("");
7747 // use the common interface palette
7748 multi_common_set_palette();
7750 // don't select any player yet.
7751 Multi_sync_player_select = -1;
7753 // determine how many of the 5 buttons to create
7754 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
7755 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
7757 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
7759 // create the interface buttons
7760 for(idx=0; idx<Multi_sync_button_count; idx++){
7761 // create the object
7762 Multi_sync_buttons[gr_screen.res][idx].button.create(&Multi_sync_window, "", Multi_sync_buttons[gr_screen.res][idx].x, Multi_sync_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
7764 // set the sound to play when highlighted
7765 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7767 // set the ani for the button
7768 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
7769 // so we have to load in frame 0, too (the file should exist)
7770 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
7771 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
7773 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
7777 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
7781 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
7782 // don't create the "launch" button text just yet
7783 if(idx == MST_LAUNCH) {
7786 // multiplayer clients should ignore the kick button
7787 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
7791 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
7794 // create the player list select button and hide it
7795 Multi_sync_plist_button.create(&Multi_sync_window, "", Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD], Ms_status_coords[gr_screen.res][MS_W_COORD], Ms_status_coords[gr_screen.res][MS_H_COORD], 0, 1);
7796 Multi_sync_plist_button.hide();
7798 // set up hotkeys for certain common functions
7799 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
7802 void multi_sync_common_do()
7804 int k = chatbox_process();
7805 k = Multi_sync_window.process(k);
7807 // process the player list
7808 multi_sync_handle_plist();
7810 // process any button clicks
7811 multi_sync_check_buttons();
7813 // process any keypresses
7817 gamesnd_play_iface(SND_USER_SELECT);
7818 multi_quit_game(PROMPT_ALL);
7823 void multi_sync_common_close()
7825 // unload any bitmaps
7826 if(!bm_unload(Multi_sync_bitmap)){
7827 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
7830 extern int Player_multi_died_check;
7831 Player_multi_died_check = -1;
7833 // destroy the UI_WINDOW
7834 Multi_sync_window.destroy();
7837 void multi_sync_blit_screen_all()
7844 // display any text in the info area
7845 multi_common_render_text();
7847 // display any pending notification messages
7848 multi_common_notify_do();
7850 // display any info about visible players
7852 for(idx=0;idx<MAX_PLAYERS;idx++){
7853 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7854 // display his name and status
7855 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
7857 // get the player state
7858 state = Net_players[idx].state;
7860 // if we're ingame joining, show all other players except myself as "playing"
7861 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
7862 state = NETPLAYER_STATE_IN_MISSION;
7866 case NETPLAYER_STATE_MISSION_LOADING:
7867 multi_sync_display_status(XSTR("Mission Loading",802),count);
7869 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
7870 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
7872 case NETPLAYER_STATE_DEBRIEF:
7873 multi_sync_display_status(XSTR("Debriefing",804),count);
7875 case NETPLAYER_STATE_MISSION_SYNC:
7876 multi_sync_display_status(XSTR("Mission Sync",805),count);
7878 case NETPLAYER_STATE_JOINING:
7879 multi_sync_display_status(XSTR("Joining",806),count);
7881 case NETPLAYER_STATE_JOINED:
7882 multi_sync_display_status(XSTR("Joined",807),count);
7884 case NETPLAYER_STATE_SLOT_ACK :
7885 multi_sync_display_status(XSTR("Slot Ack",808),count);
7887 case NETPLAYER_STATE_BRIEFING:
7888 multi_sync_display_status(XSTR("Briefing",765),count);
7890 case NETPLAYER_STATE_SHIP_SELECT:
7891 multi_sync_display_status(XSTR("Ship Select",809),count);
7893 case NETPLAYER_STATE_WEAPON_SELECT:
7894 multi_sync_display_status(XSTR("Weapon Select",810),count);
7896 case NETPLAYER_STATE_WAITING:
7897 multi_sync_display_status(XSTR("Waiting",811),count);
7899 case NETPLAYER_STATE_IN_MISSION:
7900 multi_sync_display_status(XSTR("In Mission",812),count);
7902 case NETPLAYER_STATE_MISSION_LOADED:
7903 multi_sync_display_status(XSTR("Mission Loaded",813),count);
7905 case NETPLAYER_STATE_DATA_LOAD:
7906 multi_sync_display_status(XSTR("Data loading",814),count);
7908 case NETPLAYER_STATE_SETTINGS_ACK:
7909 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
7911 case NETPLAYER_STATE_INGAME_SHIPS:
7912 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
7914 case NETPLAYER_STATE_INGAME_WINGS:
7915 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
7917 case NETPLAYER_STATE_INGAME_RPTS:
7918 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
7920 case NETPLAYER_STATE_SLOTS_ACK:
7921 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
7923 case NETPLAYER_STATE_POST_DATA_ACK:
7924 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
7926 case NETPLAYER_STATE_FLAG_ACK :
7927 multi_sync_display_status(XSTR("Flags Ack",821),count);
7929 case NETPLAYER_STATE_MT_STATS :
7930 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
7932 case NETPLAYER_STATE_WSS_ACK :
7933 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
7935 case NETPLAYER_STATE_HOST_SETUP :
7936 multi_sync_display_status(XSTR("Host setup",824),count);
7938 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
7939 multi_sync_display_status(XSTR("Debrief accept",825),count);
7941 case NETPLAYER_STATE_DEBRIEF_REPLAY:
7942 multi_sync_display_status(XSTR("Debrief replay",826),count);
7944 case NETPLAYER_STATE_CPOOL_ACK:
7945 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
7947 case NETPLAYER_STATE_MISSION_XFER :
7949 // server should display the pct completion of all clients
7950 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7951 if(Net_players[idx].s_info.xfer_handle != -1){
7952 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
7954 // if we've got a valid xfer handle
7955 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7956 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7960 strcpy(txt,XSTR("Mission file xfer",829));
7963 strcpy(txt,XSTR("Mission file xfer",829));
7966 // clients should display only for themselves (which is the only thing they know)
7968 // if we've got a valid file xfer handle
7969 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
7970 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
7972 // if we've got a valid xfer handle
7973 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7974 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7978 strcpy(txt,XSTR("Mission file xfer",829));
7983 strcpy(txt,XSTR("Mission file xfer",829));
7988 multi_sync_display_status(txt,count);
7991 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
7998 // display the mission start countdown timer (if any)
7999 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8001 // process and show the chatbox thingie
8005 Multi_sync_window.draw_tooltip();
8007 // display the voice status indicator
8008 multi_common_voice_display_status();
8011 void multi_sync_check_buttons()
8014 for(idx=0;idx<Multi_sync_button_count;idx++){
8015 // we only really need to check for one button pressed at a time, so we can break after
8017 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8018 multi_sync_button_pressed(idx);
8024 void multi_sync_button_pressed(int n)
8029 gamesnd_play_iface(SND_USER_SELECT);
8030 multi_quit_game(PROMPT_ALL);
8033 // scroll the info box up
8034 case MS_SCROLL_INFO_UP:
8035 multi_common_scroll_text_up();
8038 // scroll the info box down
8039 case MS_SCROLL_INFO_DOWN:
8040 multi_common_scroll_text_down();
8045 // if we have a currently selected player, kick him
8046 if(Multi_sync_player_select >= 0){
8047 multi_kick_player(Multi_sync_player_select);
8051 // start the final launch countdown (post-sync only)
8053 multi_sync_start_countdown();
8056 // doesn't do anything
8062 void multi_sync_pre_init()
8066 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8068 // if we're in teamplay mode, always force skill level to be medium
8069 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8070 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8071 Game_skill_level = NUM_SKILL_LEVELS / 2;
8072 multi_options_update_netgame();
8075 // notify everyone of when we get here
8076 if(!(Game_mode & GM_STANDALONE_SERVER)){
8077 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8078 send_netplayer_update_packet();
8081 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8083 ml_string(NOX("Server performing pre-briefing data sync"));
8085 if(!(Game_mode & GM_STANDALONE_SERVER)){
8086 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8089 // maybe initialize tvt and squad war stuff
8090 if(Netgame.type_flags & NG_TYPE_TEAM){
8091 multi_team_level_init();
8094 // force everyone into this state
8095 send_netgame_update_packet();
8097 if(!(Game_mode & GM_STANDALONE_SERVER)){
8098 multi_common_add_text(XSTR("Send update packet\n",831),1);
8101 // setup some of my own data
8102 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8104 // do any output stuff
8105 if(Game_mode & GM_STANDALONE_SERVER){
8106 std_debug_set_standalone_state_string("Mission Sync");
8109 // do this here to insure we have the most up to date file checksum info
8110 multi_get_mission_checksum(Game_current_mission_filename);
8111 // parse_get_file_signature(Game_current_mission_filename);
8113 if(!(Game_mode & GM_STANDALONE_SERVER)){
8114 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8117 if(!(Game_mode & GM_STANDALONE_SERVER)){
8118 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8122 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8123 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8124 for(idx=0;idx<MAX_PLAYERS;idx++){
8125 Net_players[idx].p_info.team = 0;
8126 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8130 // we aren't necessarily xferring the mission file yet
8131 Assert(Net_player->s_info.xfer_handle == -1);
8133 // always call this for good measure
8134 multi_campaign_flush_data();
8136 Mission_sync_flags = 0;
8137 Multi_mission_loaded = 0;
8140 void multi_sync_pre_do()
8144 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8145 // all servers (standalone or no, go through this)
8146 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8147 // wait for everyone to arrive, then request filesig from all of them
8148 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8149 send_file_sig_request(Netgame.mission_name);
8150 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8152 if(!(Game_mode & GM_STANDALONE_SERVER)){
8153 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8157 // if we're waiting for players to receive files, then check on their status
8158 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8159 for(idx=0;idx<MAX_PLAYERS;idx++){
8160 // if this player is in the process of xferring a file
8161 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8162 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8163 // if it has successfully completed, set his ok flag
8164 case MULTI_XFER_SUCCESS :
8166 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8168 // release the xfer instance handle
8169 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8170 Net_players[idx].s_info.xfer_handle = -1;
8172 // if it has failed or timed-out, kick the player
8173 case MULTI_XFER_TIMEDOUT:
8174 case MULTI_XFER_FAIL:
8175 // release the xfer handle
8176 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8177 Net_players[idx].s_info.xfer_handle = -1;
8180 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8187 // NOTE : this is now obsolete
8188 // once everyone is verified, do any data transfer necessary
8189 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8190 // do nothing for now
8191 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8193 // send campaign pool data
8194 multi_campaign_send_pool_status();
8197 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8198 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8199 // check to see if everyone has acked the campaign pool data
8200 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8201 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8205 // once everyone is verified, tell them to load the mission
8206 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8207 // move along faster
8208 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8209 send_netplayer_load_packet(NULL);
8210 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8212 if(!(Game_mode & GM_STANDALONE_SERVER)){
8213 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8216 // load the mission myself, as soon as possible
8217 if(!Multi_mission_loaded){
8218 nprintf(("Network","Server loading mission..."));
8220 // update everyone about my status
8221 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8222 send_netplayer_update_packet();
8224 game_start_mission();
8226 nprintf(("Network","Done\n"));
8227 Multi_mission_loaded = 1;
8229 // update everyone about my status
8230 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8231 send_netplayer_update_packet();
8233 if(!(Game_mode & GM_STANDALONE_SERVER)){
8234 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8239 // if everyone has loaded the mission, randomly assign players to ships
8240 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8241 // call the team select function to assign players to their ships, wings, etc
8242 multi_ts_assign_players_all();
8243 send_netplayer_slot_packet();
8246 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8249 // if everyone has loaded the mission, move to the team select stage
8250 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8251 Netgame.game_state = NETGAME_STATE_BRIEFING;
8252 send_netgame_update_packet(); // this will push everyone into the next state
8254 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8255 // team select state
8256 if(Game_mode & GM_STANDALONE_SERVER){
8257 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8259 gameseq_post_event(GS_EVENT_START_GAME);
8262 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8264 if(!(Game_mode & GM_STANDALONE_SERVER)){
8265 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8269 // clients should detect here if they are doing a file xfer and do error processing
8270 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8271 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8272 // if it has successfully completed, set his ok flag
8273 case MULTI_XFER_SUCCESS :
8274 // release my xfer handle
8275 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8276 Net_player->s_info.xfer_handle = -1;
8279 // if it has failed or timed-out, kick the player
8280 case MULTI_XFER_TIMEDOUT:
8281 case MULTI_XFER_FAIL:
8282 // release my xfer handle
8283 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8284 Net_player->s_info.xfer_handle = -1;
8286 // leave the game qith an error code
8287 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8294 if(!(Game_mode & GM_STANDALONE_SERVER)){
8296 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8297 if(Multi_sync_bitmap != -1){
8298 gr_set_bitmap(Multi_sync_bitmap);
8301 Multi_sync_window.draw();
8303 multi_sync_blit_screen_all();
8309 void multi_sync_pre_close()
8311 // at this point, we should shut down any file xfers...
8312 if(Net_player->s_info.xfer_handle != -1){
8313 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8315 multi_xfer_abort(Net_player->s_info.xfer_handle);
8316 Net_player->s_info.xfer_handle;
8320 void multi_sync_post_init()
8322 multi_reset_timestamps();
8324 Multi_state_timestamp = timestamp(0);
8327 ml_string(NOX("Performing post-briefing data sync"));
8329 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8330 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8332 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8335 // everyone should re-initialize these
8336 init_multiplayer_stats();
8338 // reset all sequencing info
8339 multi_oo_reset_sequencing();
8341 // if I am not the master of the game, then send the firing information for my ship
8343 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8344 send_firing_info_packet();
8347 // if I'm not a standalone server, load up the countdown stuff
8348 if(!(Game_mode & GM_STANDALONE_SERVER)){
8349 Multi_sync_countdown_anim = NULL;
8350 Multi_sync_countdown_instance = NULL;
8351 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8352 if(Multi_sync_countdown_anim == NULL){
8353 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8357 // create objects for all permanent observers
8358 multi_obs_level_init();
8360 // clear the game start countdown timer
8361 Multi_sync_countdown_timer = -1.0f;
8362 Multi_sync_countdown = -1;
8364 // if this is a team vs. team mission, mark all ship teams appropriately
8365 if(Netgame.type_flags & NG_TYPE_TEAM){
8366 multi_team_mark_all_ships();
8369 Mission_sync_flags = 0;
8370 Multi_sync_launch_pressed = 0;
8373 #define MULTI_POST_TIMESTAMP 7000
8375 extern int create_wings();
8377 void multi_sync_post_do()
8381 // only if the host is also the master should he be doing this (non-standalone situation)
8382 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8384 // once everyone gets to this screen, send them the ship classes of all ships.
8385 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8386 // only the host should ever do this
8387 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8388 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8389 multi_ts_create_wings();
8391 // update player ets settings
8392 for(idx=0;idx<MAX_PLAYERS;idx++){
8393 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8394 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8399 // note that this is done a little differently for standalones and nonstandalones
8400 send_post_sync_data_packet();
8402 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8404 Mission_sync_flags |= MS_FLAG_POST_DATA;
8407 // send weapon slots data
8408 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8409 // note that this is done a little differently for standalones and nonstandalones
8410 if(Netgame.type_flags & NG_TYPE_TEAM){
8411 send_wss_slots_data_packet(0,0);
8412 send_wss_slots_data_packet(1,1);
8414 send_wss_slots_data_packet(0,1);
8417 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8419 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8422 // once weapon information is received, send player settings info
8423 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8424 send_player_settings_packet();
8426 // server (specifically, the standalone), should set this here
8427 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8428 send_netplayer_update_packet();
8430 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8432 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8435 // check to see if the countdown timer has started and act appropriately
8436 if( Multi_sync_countdown_timer > -1.0f ) {
8438 // increment by frametime.
8439 Multi_sync_countdown_timer += flFrametime;
8441 // if the animation is not playing, start it
8442 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8443 anim_play_struct aps;
8445 anim_play_init(&aps, Multi_sync_countdown_anim, Multi_sync_countdown_coords[gr_screen.res][MS_X_COORD], Multi_sync_countdown_coords[gr_screen.res][MS_Y_COORD]);
8446 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8447 aps.framerate_independent = 1;
8449 Multi_sync_countdown_instance = anim_play(&aps);
8452 // if the next second has expired
8453 if( Multi_sync_countdown_timer >= 1.0f ) {
8455 Multi_sync_countdown--;
8456 Multi_sync_countdown_timer = 0.0f;
8458 // if the countdown has reached 0, launch the mission
8459 if(Multi_sync_countdown == 0){
8460 Multi_sync_countdown_timer = -1.0f;
8462 Multi_sync_launch_pressed = 0;
8463 multi_sync_launch();
8465 // otherwise send a countdown packet
8467 send_countdown_packet(Multi_sync_countdown);
8472 // jump into the mission myself
8473 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8474 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8475 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8476 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8477 gameseq_post_event(GS_EVENT_ENTER_GAME);
8479 multi_common_add_text(XSTR("Moving into game\n",842),1);
8483 // maybe start the animation countdown
8484 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8485 anim_play_struct aps;
8487 anim_play_init(&aps, Multi_sync_countdown_anim, Multi_sync_countdown_coords[gr_screen.res][MS_X_COORD], Multi_sync_countdown_coords[gr_screen.res][MS_Y_COORD]);
8488 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8489 aps.framerate_independent = 1;
8491 Multi_sync_countdown_instance = anim_play(&aps);
8495 // host - specific stuff
8496 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8497 // create the launch button so the host can click
8498 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8499 multi_sync_create_launch_button();
8504 if(!(Game_mode & GM_STANDALONE_SERVER)){
8506 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8507 if(Multi_sync_bitmap != -1){
8508 gr_set_bitmap(Multi_sync_bitmap);
8511 Multi_sync_window.draw();
8513 multi_sync_blit_screen_all();
8519 void multi_sync_post_close()
8523 // if I'm not a standalone server, unload up the countdown stuff
8524 if(!(Game_mode & GM_STANDALONE_SERVER)){
8525 // release all rendering animation instances (should only be 1)
8526 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8527 Multi_sync_countdown_instance = NULL;
8529 // free up the countdown animation
8530 if(Multi_sync_countdown_anim != NULL){
8531 anim_free(Multi_sync_countdown_anim);
8532 Multi_sync_countdown_anim = NULL;
8536 // all players should reset sequencing
8537 for(idx=0;idx<MAX_PLAYERS;idx++){
8538 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8539 Net_players[idx].client_cinfo_seq = 0;
8540 Net_players[idx].client_server_seq = 0;
8544 // multiplayer dogfight
8545 multi_df_level_pre_enter();
8547 // clients should clear obj_pair array and add pair for themselves
8549 if ( MULTIPLAYER_CLIENT ) {
8551 obj_add_pairs( OBJ_INDEX(Player_obj) );
8556 void multi_sync_display_name(char *name,int index,int np_index)
8558 char fit[CALLSIGN_LEN];
8560 // make sure the string actually fits
8563 // if we're in team vs. team mode
8564 if(Netgame.type_flags & NG_TYPE_TEAM){
8565 gr_force_fit_string(fit,CALLSIGN_LEN, Ms_status2_coords[gr_screen.res][MS_X_COORD] - Ms_status_coords[gr_screen.res][MS_X_COORD] - 20 - Ms_cd_icon_offset[gr_screen.res] - Ms_team_icon_offset[gr_screen.res]);
8567 // if this is the currently selected player, draw him highlighted
8568 if(np_index == Multi_sync_player_select){
8569 gr_set_color_fast(&Color_text_selected);
8571 gr_set_color_fast(&Color_text_normal);
8575 gr_string(Ms_status_coords[gr_screen.res][0] + Ms_cd_icon_offset[gr_screen.res] + Ms_team_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10),fit);
8577 // blit his team icon
8579 if(Net_players[np_index].p_info.team == 0){
8580 // blit the team captain icon
8581 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8582 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8583 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
8584 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8587 // normal team member icon
8589 if(Multi_common_icons[MICON_TEAM0] != -1){
8590 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
8591 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8596 else if(Net_players[np_index].p_info.team == 1){
8597 // blit the team captain icon
8598 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8599 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8600 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
8601 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8604 // normal team member icon
8606 if(Multi_common_icons[MICON_TEAM1] != -1){
8607 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
8608 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8613 gr_force_fit_string(fit, CALLSIGN_LEN, Ms_status2_coords[gr_screen.res][MS_X_COORD] - Ms_status_coords[gr_screen.res][MS_X_COORD] - 20 - Ms_cd_icon_offset[gr_screen.res]);
8615 // if this is the currently selected player, draw him highlighted
8616 if(np_index == Multi_sync_player_select){
8617 gr_set_color_fast(&Color_text_selected);
8619 gr_set_color_fast(&Color_text_normal);
8623 gr_string(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10),fit);
8626 // maybe blit his CD status icon
8627 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8628 gr_set_bitmap(Multi_common_icons[MICON_CD]);
8629 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8633 void multi_sync_display_status(char *status,int index)
8637 // make sure the string actually fits
8638 strcpy(fit, status);
8639 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8640 gr_set_color_fast(&Color_bright);
8641 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
8644 void multi_sync_force_start_pre()
8647 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
8649 // go through the player list and boot anyone who isn't in the right state
8650 for(idx=0;idx<MAX_PLAYERS;idx++){
8651 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
8652 multi_kick_player(idx,0);
8657 void multi_sync_force_start_post()
8661 int num_kill_states;
8663 // determine the state we want all players in so that we can find those who are not in the state
8664 kill_state[0] = NETPLAYER_STATE_BRIEFING;
8665 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
8666 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
8667 num_kill_states = 3;
8669 // go through the player list and boot anyone who isn't in the right state
8670 for(idx=0;idx<MAX_PLAYERS;idx++){
8671 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
8672 // check against all kill state
8673 for(idx2 = 0;idx2<num_kill_states;idx2++){
8674 if(Net_players[idx].state == kill_state[idx2]){
8675 multi_kick_player(idx,0);
8683 void multi_sync_start_countdown()
8685 // don't allow repeat button presses
8686 if(Multi_sync_launch_pressed){
8690 Multi_sync_launch_pressed = 1;
8692 // if I'm the server, begin the countdown
8693 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8694 gamesnd_play_iface(SND_COMMIT_PRESSED);
8695 Multi_sync_countdown_timer = 0.0f;
8696 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
8698 // send an initial countdown value
8699 send_countdown_packet(Multi_sync_countdown);
8701 // otherwise send the "start countdown" packet to the standalone
8703 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
8704 send_countdown_packet(-1);
8708 void multi_sync_launch()
8710 // don't allow repeat button presses
8711 if(Multi_sync_launch_pressed){
8715 Multi_sync_launch_pressed = 1;
8718 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
8720 // tell everyone to jump into the mission
8721 send_jump_into_mission_packet();
8722 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
8724 // set the # of players at the start of the mission
8725 Multi_num_players_at_start = multi_num_players();
8726 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
8728 // initialize datarate limiting for all clients
8729 multi_oo_rate_init_all();
8731 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
8734 void multi_sync_create_launch_button()
8736 if (!Multi_launch_button_created) {
8737 // create the object
8738 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.create(&Multi_sync_window, "", Multi_sync_buttons[gr_screen.res][MS_LAUNCH].x, Multi_sync_buttons[gr_screen.res][MS_LAUNCH].y, 1, 1, 0, 1);
8740 // set the sound to play when highlighted
8741 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
8743 // set the ani for the button
8744 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
8747 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
8750 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
8752 // create the text for the button
8753 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
8755 // increment the button count so we start checking this one
8756 Multi_sync_button_count++;
8758 Multi_launch_button_created = 1;
8762 void multi_sync_handle_plist()
8768 // if we don't have a currently selected player, select one
8769 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
8770 for(idx=0;idx<MAX_PLAYERS;idx++){
8771 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8772 Multi_sync_player_select = idx;
8778 // check for button list presses
8779 if(Multi_sync_plist_button.pressed()){
8780 // get the y mouse coords
8781 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
8783 // get the index of the item selected
8784 select_index = my / 10;
8786 // if the index is greater than the current # connections, do nothing
8787 if(select_index > (multi_num_connections() - 1)){
8791 // translate into an absolute Net_players[] index (get the Nth net player)
8792 Multi_sync_player_select = -1;
8793 for(idx=0;idx<MAX_PLAYERS;idx++){
8794 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8798 // if we've found the item we're looking for
8799 if(select_index < 0){
8800 Multi_sync_player_select = idx;
8805 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
8807 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
8808 Multi_sync_player_select = -1;
8814 // -------------------------------------------------------------------------------------------------------------
8816 // MULTIPLAYER DEBRIEF SCREEN
8819 // other relevant data
8820 int Multi_debrief_accept_hit;
8821 int Multi_debrief_replay_hit;
8823 // set if the server has left the game
8824 int Multi_debrief_server_left = 0;
8826 // if we've reported on TvT status all players are in the debrief
8827 int Multi_debrief_reported_tvt = 0;
8829 // whether stats are being accepted
8830 // -1 == no decision yet
8833 int Multi_debrief_stats_accept_code = -1;
8835 int Multi_debrief_server_framecount = 0;
8837 float Multi_debrief_time = 0.0f;
8838 float Multi_debrief_resend_time = 10.0f;
8840 void multi_debrief_init()
8844 Multi_debrief_time = 0.0f;
8845 Multi_debrief_resend_time = 10.0f;
8847 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
8848 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
8849 Net_player->state = NETPLAYER_STATE_DEBRIEF;
8850 send_netplayer_update_packet();
8853 // unflag some stuff
8854 for(idx=0;idx<MAX_PLAYERS;idx++){
8855 if(MULTI_CONNECTED(Net_players[idx])){
8856 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
8860 // if text input mode is active, clear it
8861 multi_msg_text_flush();
8863 // the server has not left yet
8864 Multi_debrief_server_left = 0;
8866 // have not hit accept or replay yet
8867 Multi_debrief_accept_hit = 0;
8868 Multi_debrief_replay_hit = 0;
8870 // stats have not been accepted yet
8871 Multi_debrief_stats_accept_code = -1;
8873 // mark stats as not being store yet
8874 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
8876 // no report on TvT yet
8877 Multi_debrief_reported_tvt = 0;
8879 Multi_debrief_server_framecount = 0;
8882 void multi_debrief_do_frame()
8884 Multi_debrief_time += flFrametime;
8886 // set the netgame state to be debriefing when appropriate
8887 if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Netgame.game_state != NETGAME_STATE_DEBRIEF) && multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY)){
8888 Netgame.game_state = NETGAME_STATE_DEBRIEF;
8889 send_netgame_update_packet();
8892 // evaluate all server stuff
8893 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8894 multi_debrief_server_process();
8898 void multi_debrief_close()
8900 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
8901 gamesnd_play_iface( SND_COMMIT_PRESSED );
8905 // handle optional mission loop
8906 void multi_maybe_set_mission_loop()
8908 int cur = Campaign.current_mission;
8909 if (Campaign.missions[cur].has_mission_loop) {
8910 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
8912 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
8914 // check for (1) mission loop available, (2) dont have to repeat last mission
8915 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
8918 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
8920 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
8922 Campaign.loop_enabled = 1;
8923 Campaign.next_mission = Campaign.loop_mission;
8928 // handle all cases for when the accept key is hit in a multiplayer debriefing
8929 void multi_debrief_accept_hit()
8931 // if we already accepted, do nothing
8932 // but he may need to hit accept again after the server has left the game, so allow this
8933 if(Multi_debrief_accept_hit){
8937 // mark this so that we don't hit it again
8938 Multi_debrief_accept_hit = 1;
8940 gamesnd_play_iface(SND_COMMIT_PRESSED);
8942 // if the server has left the game, always just end the game.
8943 if(Multi_debrief_server_left){
8944 if(!multi_quit_game(PROMPT_ALL)){
8945 Multi_debrief_server_left = 1;
8946 Multi_debrief_accept_hit = 0;
8948 Multi_debrief_server_left = 0;
8951 // query the host and see if he wants to accept stats
8952 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8953 // if we're on a tracker game, he gets no choice for storing stats
8954 if(MULTI_IS_TRACKER_GAME){
8955 multi_maybe_set_mission_loop();
8957 int res = popup(PF_TITLE | PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_IGNORE_ESC,3,XSTR("&Cancel",779),XSTR("&Accept",844),XSTR("&Toss",845),XSTR("(Continue Netgame)\nDo you wish to accept these stats?",846));
8959 // evaluate the result
8964 Multi_debrief_accept_hit = 0;
8967 // set the accept code to be "not accepting"
8969 multi_debrief_stats_toss();
8970 multi_maybe_set_mission_loop();
8973 // accept the stats and continue
8975 multi_debrief_stats_accept();
8976 multi_maybe_set_mission_loop();
8982 // set my netplayer state to be "debrief_accept", and be done with it
8983 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
8984 send_netplayer_update_packet();
8988 // handle all cases for when the escape key is hit in a multiplayer debriefing
8989 void multi_debrief_esc_hit()
8993 // if the server has left
8994 if(Multi_debrief_server_left){
8995 multi_quit_game(PROMPT_ALL);
9000 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9001 // if the stats have already been accepted
9002 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9003 multi_quit_game(PROMPT_HOST);
9005 res = popup(PF_TITLE | PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_IGNORE_ESC,3,XSTR("&Cancel",779),XSTR("&Accept",844),XSTR("&Toss",845),XSTR("(Exit Netgame)\nDo you wish to accept these stats?",847));
9007 // evaluate the result
9014 // set the accept code to be "not accepting"
9016 multi_debrief_stats_toss();
9017 multi_quit_game(PROMPT_NONE);
9020 // accept the stats and continue
9022 multi_debrief_stats_accept();
9023 multi_quit_game(PROMPT_NONE);
9028 // if the stats haven't been accepted yet, or this is a tracker game
9029 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9030 res = popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON,2,XSTR("&Cancel",779),XSTR("&Leave",848),XSTR("Are you sure you want to leave the netgame before stats are stored?",849));
9032 // evaluate the result
9034 multi_quit_game(PROMPT_NONE);
9037 // otherwise go through the normal endgame channels
9039 multi_quit_game(PROMPT_ALL);
9044 void multi_debrief_replay_hit()
9046 // only the host should ever get here
9047 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9049 // if the button was already pressed, do nothing
9050 if(Multi_debrief_accept_hit){
9054 // same as hittin the except button except no stats are kept
9055 Multi_debrief_accept_hit = 1;
9057 // mark myself as being in the replay state so we know what to do next
9058 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9059 send_netplayer_update_packet();
9062 // call this when the server has left and we would otherwise be saying "contact lost with server
9063 void multi_debrief_server_left()
9066 Multi_debrief_server_left = 1;
9068 // undo any "accept" hit so that clients can hit accept again to leave
9069 Multi_debrief_accept_hit = 0;
9072 void multi_debrief_stats_accept()
9074 // don't do anything if we've already accepted
9075 if(Multi_debrief_stats_accept_code != -1){
9079 Multi_debrief_stats_accept_code = 1;
9081 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9082 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9083 // send a packet to the players telling them to store their stats
9084 send_store_stats_packet(1);
9087 // add a chat line saying "stats have been accepted"
9088 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9091 ml_string(NOX("Stats stored"));
9094 void multi_debrief_stats_toss()
9096 // don't do anything if we've already accepted
9097 if(Multi_debrief_stats_accept_code != -1){
9101 Multi_debrief_stats_accept_code = 0;
9103 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9104 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9105 // send a packet to the players telling them to store their stats
9106 send_store_stats_packet(0);
9109 // add a chat line saying "stats have been accepted"
9110 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9113 ml_string(NOX("Stats tossed"));
9116 int multi_debrief_stats_accept_code()
9118 return Multi_debrief_stats_accept_code;
9121 void multi_debrief_server_process()
9124 int player_status,other_status;
9126 Multi_debrief_server_framecount++;
9128 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9129 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9130 // find all players who are not in the debrief state and hit them with the endgame packet
9131 for(idx=0; idx<MAX_PLAYERS; idx++){
9132 if( MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && ((Net_players[idx].state != NETPLAYER_STATE_DEBRIEF) || (Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT) || (Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_REPLAY)) ){
9133 send_endgame_packet(&Net_players[idx]);
9138 Multi_debrief_resend_time += 7.0f;
9141 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9144 // check all players
9145 for(idx=0;idx<MAX_PLAYERS;idx++){
9146 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9147 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9154 // if we haven't already reported TvT results
9155 if(multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY) && (Netgame.type_flags & NG_TYPE_TEAM) && !Multi_debrief_reported_tvt && (Multi_debrief_server_framecount > 4)){
9156 multi_team_report();
9157 Multi_debrief_reported_tvt = 1;
9160 // if all other players are good to go, check the host
9162 // if he is ready to continue
9163 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9166 // if he wants to replay the mission
9167 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9170 // if he is not ready
9175 // if all players are _not_ good to go
9180 // if we're in the debriefing state in a campaign mode, process accordingly
9181 if(Netgame.campaign_mode == MP_CAMPAIGN){
9182 multi_campaign_do_debrief(player_status);
9184 // otherwise process as normal (looking for all players to be ready to go to the next mission
9186 if(player_status == 1){
9187 multi_flush_mission_stuff();
9189 // set the netgame state to be forming and continue
9190 Netgame.game_state = NETGAME_STATE_FORMING;
9191 send_netgame_update_packet();
9193 // move to the proper state
9194 if(Game_mode & GM_STANDALONE_SERVER){
9195 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9197 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9200 multi_reset_timestamps();
9201 } else if(player_status == 2){
9202 multi_flush_mission_stuff();
9204 // tell everyone to move into the pre-briefing sync state
9205 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9206 send_netgame_update_packet();
9208 // move back to the mission sync screen for the same mission again
9209 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9210 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9212 multi_reset_timestamps();
9218 // -------------------------------------------------------------------------------------------------------------
9220 // MULTIPLAYER PASSWORD POPUP
9225 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9226 "Password", // GR_640
9227 "2_Password" // GR_1024
9230 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9231 "Password-M", // GR_640
9232 "2_Password-M" // GR_1024
9237 // constants for coordinate lookup
9238 #define MPWD_X_COORD 0
9239 #define MPWD_Y_COORD 1
9240 #define MPWD_W_COORD 2
9241 #define MPWD_H_COORD 3
9244 #define MULTI_PWD_NUM_BUTTONS 2
9245 #define MPWD_CANCEL 0
9246 #define MPWD_COMMIT 1
9248 // password area defs
9249 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9258 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9259 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9260 int Multi_pwd_bitmap; // the background bitmap
9261 int Multi_passwd_background = -1;
9262 int Multi_passwd_done = -1;
9263 int Multi_passwd_running = 0;
9266 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9268 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9269 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9272 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9273 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9278 #define MULTI_PWD_NUM_TEXT 3
9279 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9281 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9282 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9283 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9286 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9287 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9288 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9292 // initialize all graphics, etc
9293 void multi_passwd_init()
9297 // store the background as it currently is
9298 Multi_passwd_background = gr_save_screen();
9300 // create the interface window
9301 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9302 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9304 // load the background bitmap
9305 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9306 if(Multi_pwd_bitmap < 0){
9307 // we failed to load the bitmap - this is very bad
9311 // create the interface buttons
9312 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9313 // create the object
9314 Multi_pwd_buttons[gr_screen.res][idx].button.create(&Multi_pwd_window, "", Multi_pwd_buttons[gr_screen.res][idx].x, Multi_pwd_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
9316 // set the sound to play when highlighted
9317 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9319 // set the ani for the button
9320 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9323 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9327 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9328 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9331 // create the password input box
9332 Multi_pwd_passwd.create(&Multi_pwd_window, Mpwd_coords[gr_screen.res][MPWD_X_COORD], Mpwd_coords[gr_screen.res][MPWD_Y_COORD],Mpwd_coords[gr_screen.res][MPWD_W_COORD], MAX_PASSWD_LEN, "", UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_INVIS, -1, &Color_normal);
9333 Multi_pwd_passwd.set_focus();
9335 // link the enter key to ACCEPT
9336 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9338 Multi_passwd_done = -1;
9339 Multi_passwd_running = 1;
9342 // close down all graphics, etc
9343 void multi_passwd_close()
9345 // unload any bitmaps
9346 bm_release(Multi_pwd_bitmap);
9348 // destroy the UI_WINDOW
9349 Multi_pwd_window.destroy();
9351 // free up the saved background screen
9352 if(Multi_passwd_background >= 0){
9353 gr_free_screen(Multi_passwd_background);
9354 Multi_passwd_background = -1;
9357 Multi_passwd_running = 0;
9360 // process any button pressed
9361 void multi_passwd_process_buttons()
9363 // if the accept button was pressed
9364 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9365 gamesnd_play_iface(SND_USER_SELECT);
9366 Multi_passwd_done = 1;
9369 // if the cancel button was pressed
9370 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9371 gamesnd_play_iface(SND_USER_SELECT);
9372 Multi_passwd_done = 0;
9376 // run the passwd popup
9377 void multi_passwd_do(char *passwd)
9381 while(Multi_passwd_done == -1){
9382 // set frametime and run background stuff
9383 game_set_frametime(-1);
9384 game_do_state_common(gameseq_get_state());
9386 k = Multi_pwd_window.process();
9388 // process any keypresses
9391 // set this to indicate the user has cancelled for one reason or another
9392 Multi_passwd_done = 0;
9396 // if the input box text has changed
9397 if(Multi_pwd_passwd.changed()){
9399 Multi_pwd_passwd.get_text(passwd);
9402 // process any button pressed
9403 multi_passwd_process_buttons();
9405 // draw the background, etc
9408 if(Multi_passwd_background >= 0){
9409 gr_restore_screen(Multi_passwd_background);
9411 gr_set_bitmap(Multi_pwd_bitmap);
9413 Multi_pwd_window.draw();
9420 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9421 int multi_passwd_popup(char *passwd)
9423 // if the popup is already running for some reason, don't do anything
9424 if(Multi_passwd_running){
9428 // initialize all graphics
9429 multi_passwd_init();
9432 multi_passwd_do(passwd);
9434 // shut everything down
9435 multi_passwd_close();
9437 return Multi_passwd_done;